{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "JHSdxPJuN4x7",
    "outputId": "5893c7f2-1dd8-4f42-dbb1-014468a68865",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:57:28.891880Z",
     "start_time": "2024-05-08T02:57:28.120648200Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sys.version_info(major=3, minor=9, micro=7, releaselevel='final', serial=0)\n",
      "matplotlib 3.8.4\n",
      "numpy 1.26.4\n",
      "pandas 2.2.2\n",
      "sklearn 1.4.2\n",
      "torch 2.2.2+cpu\n",
      "cpu\n"
     ]
    }
   ],
   "source": [
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline\n",
    "import numpy as np\n",
    "import sklearn\n",
    "import pandas as pd\n",
    "import os\n",
    "import sys\n",
    "import time\n",
    "from tqdm.auto import tqdm\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "print(sys.version_info)\n",
    "for module in mpl, np, pd, sklearn, torch:\n",
    "    print(module.__name__, module.__version__)\n",
    "\n",
    "device = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n",
    "print(device)\n",
    "\n",
    "seed = 42\n",
    "torch.manual_seed(seed)\n",
    "torch.cuda.manual_seed_all(seed)\n",
    "np.random.seed(seed)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "55aNBForN4x9"
   },
   "source": [
    "## 数据加载\n",
    "\n",
    "- 采用WMT16的德语和英语平行语料库，数据集主页：[WMT16](https://www.statmt.org/wmt16/multimodal-task.html#task1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "SkjBEbr1N4x-",
    "outputId": "b3cf128b-0d04-40da-864c-74f2090222fd",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:27.108933Z",
     "start_time": "2024-05-08T02:57:28.147634200Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "ERROR: Invalid requirement: '#和jieba分词类似'\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple\n",
      "Requirement already satisfied: subword-nmt in c:\\users\\administrator\\envs\\ml\\lib\\site-packages (0.3.8)\n",
      "Requirement already satisfied: mock in c:\\users\\administrator\\envs\\ml\\lib\\site-packages (from subword-nmt) (5.1.0)\n",
      "Requirement already satisfied: tqdm in c:\\users\\administrator\\envs\\ml\\lib\\site-packages (from subword-nmt) (4.66.2)\n",
      "Requirement already satisfied: colorama in c:\\users\\administrator\\envs\\ml\\lib\\site-packages (from tqdm->subword-nmt) (0.4.6)\n",
      "[train] 源语言文本分词完成\n",
      "[train] 目标语言文本分词完成\n",
      "[val] 源语言文本分词完成\n",
      "[val] 目标语言文本分词完成\n",
      "[test] 源语言文本分词完成\n",
      "[test] 目标语言文本分词完成\n",
      "Finished applying bpe to train files.\n",
      "Finished applying bpe to val files.\n",
      "Finished applying bpe to test files.\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "\n",
      "  0%|          | 0/10000 [00:00<?, ?it/s]\n",
      "  0%|          | 6/10000 [00:00<02:55, 56.82it/s]\n",
      "  0%|          | 12/10000 [00:00<03:56, 42.18it/s]\n",
      "  0%|          | 21/10000 [00:00<03:03, 54.49it/s]\n",
      "  0%|          | 27/10000 [00:00<03:09, 52.54it/s]\n",
      "  0%|          | 43/10000 [00:00<01:59, 83.49it/s]\n",
      "  1%|          | 54/10000 [00:00<01:53, 88.00it/s]\n",
      "  1%|          | 75/10000 [00:00<01:22, 121.02it/s]\n",
      "  1%|          | 99/10000 [00:00<01:03, 155.02it/s]\n",
      "  1%|1         | 127/10000 [00:01<00:51, 191.07it/s]\n",
      "  2%|1         | 155/10000 [00:01<00:45, 214.09it/s]\n",
      "  2%|1         | 187/10000 [00:01<00:40, 244.41it/s]\n",
      "  2%|2         | 216/10000 [00:01<00:38, 257.11it/s]\n",
      "  3%|2         | 261/10000 [00:01<00:31, 311.38it/s]\n",
      "  3%|2         | 296/10000 [00:01<00:30, 321.86it/s]\n",
      "  3%|3         | 329/10000 [00:01<00:30, 317.64it/s]\n",
      "  4%|3         | 370/10000 [00:01<00:28, 343.73it/s]\n",
      "  4%|4         | 405/10000 [00:01<00:28, 336.73it/s]\n",
      "  4%|4         | 442/10000 [00:01<00:27, 344.45it/s]\n",
      "  5%|4         | 477/10000 [00:02<00:28, 330.10it/s]\n",
      "  5%|5         | 516/10000 [00:02<00:27, 346.23it/s]\n",
      "  6%|5         | 560/10000 [00:02<00:25, 365.98it/s]\n",
      "  6%|6         | 601/10000 [00:02<00:25, 374.39it/s]\n",
      "  7%|6         | 653/10000 [00:02<00:22, 415.35it/s]\n",
      "  7%|6         | 695/10000 [00:02<00:24, 380.65it/s]\n",
      "  7%|7         | 738/10000 [00:02<00:23, 392.15it/s]\n",
      "  8%|7         | 781/10000 [00:02<00:22, 401.74it/s]\n",
      "  8%|8         | 841/10000 [00:02<00:20, 454.66it/s]\n",
      "  9%|8         | 893/10000 [00:03<00:19, 471.47it/s]\n",
      "  9%|9         | 945/10000 [00:03<00:18, 484.28it/s]\n",
      " 10%|9         | 999/10000 [00:03<00:18, 497.77it/s]\n",
      " 11%|#         | 1061/10000 [00:03<00:16, 530.66it/s]\n",
      " 11%|#1        | 1115/10000 [00:03<00:16, 531.94it/s]\n",
      " 12%|#1        | 1184/10000 [00:03<00:15, 573.68it/s]\n",
      " 12%|#2        | 1242/10000 [00:03<00:17, 513.59it/s]\n",
      " 13%|#3        | 1300/10000 [00:03<00:16, 528.92it/s]\n",
      " 14%|#3        | 1354/10000 [00:03<00:16, 527.70it/s]\n",
      " 14%|#4        | 1413/10000 [00:04<00:15, 543.91it/s]\n",
      " 15%|#4        | 1483/10000 [00:04<00:14, 587.24it/s]\n",
      " 16%|#5        | 1566/10000 [00:04<00:12, 654.17it/s]\n",
      " 16%|#6        | 1646/10000 [00:04<00:12, 694.92it/s]\n",
      " 17%|#7        | 1719/10000 [00:04<00:11, 703.28it/s]\n",
      " 18%|#8        | 1814/10000 [00:04<00:10, 773.98it/s]\n",
      " 19%|#8        | 1899/10000 [00:04<00:10, 795.16it/s]\n",
      " 20%|#9        | 1988/10000 [00:04<00:09, 818.62it/s]\n",
      " 21%|##        | 2077/10000 [00:04<00:09, 832.53it/s]\n",
      " 22%|##1       | 2188/10000 [00:04<00:08, 909.36it/s]\n",
      " 23%|##2       | 2290/10000 [00:05<00:08, 939.50it/s]\n",
      " 24%|##4       | 2413/10000 [00:05<00:07, 1024.08it/s]\n",
      " 26%|##5       | 2563/10000 [00:05<00:06, 1162.28it/s]\n",
      " 27%|##7       | 2739/10000 [00:05<00:05, 1336.37it/s]\n",
      " 29%|##9       | 2933/10000 [00:05<00:04, 1511.77it/s]\n",
      " 31%|###1      | 3117/10000 [00:05<00:04, 1604.97it/s]\n",
      " 33%|###3      | 3304/10000 [00:05<00:03, 1681.46it/s]\n",
      " 35%|###5      | 3542/10000 [00:05<00:03, 1884.45it/s]\n",
      " 38%|###7      | 3761/10000 [00:06<00:05, 1164.97it/s]\n",
      " 39%|###9      | 3913/10000 [00:06<00:07, 802.97it/s] \n",
      " 40%|####      | 4032/10000 [00:06<00:09, 661.78it/s]\n",
      " 41%|####1     | 4128/10000 [00:06<00:09, 624.96it/s]\n",
      " 42%|####2     | 4210/10000 [00:07<00:09, 580.51it/s]\n",
      " 43%|####2     | 4281/10000 [00:07<00:09, 575.34it/s]\n",
      " 43%|####3     | 4348/10000 [00:07<00:10, 555.03it/s]\n",
      " 44%|####4     | 4410/10000 [00:07<00:10, 539.19it/s]\n",
      " 45%|####4     | 4468/10000 [00:07<00:10, 523.93it/s]\n",
      " 45%|####5     | 4524/10000 [00:07<00:10, 530.91it/s]\n",
      " 46%|####5     | 4579/10000 [00:07<00:10, 520.48it/s]\n",
      " 46%|####6     | 4636/10000 [00:07<00:10, 527.71it/s]\n",
      " 47%|####6     | 4690/10000 [00:08<00:10, 494.01it/s]\n",
      " 47%|####7     | 4741/10000 [00:08<00:11, 473.09it/s]\n",
      " 48%|####7     | 4789/10000 [00:08<00:11, 468.88it/s]\n",
      " 48%|####8     | 4840/10000 [00:08<00:10, 478.70it/s]\n",
      " 49%|####8     | 4889/10000 [00:08<00:11, 441.62it/s]\n",
      " 49%|####9     | 4941/10000 [00:08<00:10, 461.18it/s]\n",
      " 50%|####9     | 4988/10000 [00:08<00:11, 425.66it/s]\n",
      " 50%|#####     | 5034/10000 [00:08<00:11, 430.25it/s]\n",
      " 51%|#####     | 5078/10000 [00:08<00:11, 429.47it/s]\n",
      " 51%|#####1    | 5131/10000 [00:09<00:10, 457.11it/s]\n",
      " 52%|#####1    | 5194/10000 [00:09<00:09, 504.94it/s]\n",
      " 53%|#####2    | 5266/10000 [00:09<00:08, 565.42it/s]\n",
      " 53%|#####3    | 5324/10000 [00:09<00:08, 542.89it/s]\n",
      " 54%|#####3    | 5382/10000 [00:09<00:08, 551.97it/s]\n",
      " 54%|#####4    | 5439/10000 [00:09<00:08, 555.66it/s]\n",
      " 55%|#####4    | 5495/10000 [00:09<00:08, 532.88it/s]\n",
      " 56%|#####5    | 5562/10000 [00:09<00:07, 570.37it/s]\n",
      " 56%|#####6    | 5620/10000 [00:09<00:07, 557.17it/s]\n",
      " 57%|#####6    | 5677/10000 [00:09<00:07, 557.75it/s]\n",
      " 57%|#####7    | 5735/10000 [00:10<00:07, 562.94it/s]\n",
      " 58%|#####7    | 5792/10000 [00:10<00:07, 549.88it/s]\n",
      " 59%|#####8    | 5855/10000 [00:10<00:07, 568.15it/s]\n",
      " 59%|#####9    | 5924/10000 [00:10<00:06, 603.19it/s]\n",
      " 60%|######    | 6001/10000 [00:10<00:06, 650.28it/s]\n",
      " 61%|######    | 6067/10000 [00:11<00:25, 154.80it/s]\n",
      " 61%|######1   | 6126/10000 [00:11<00:19, 194.09it/s]\n",
      " 62%|######1   | 6196/10000 [00:11<00:15, 251.49it/s]\n",
      " 63%|######2   | 6268/10000 [00:12<00:11, 316.83it/s]\n",
      " 64%|######3   | 6353/10000 [00:12<00:09, 403.99it/s]\n",
      " 64%|######4   | 6435/10000 [00:12<00:07, 482.92it/s]\n",
      " 65%|######5   | 6507/10000 [00:12<00:13, 255.94it/s]\n",
      " 66%|######5   | 6561/10000 [00:13<00:13, 251.41it/s]\n",
      " 66%|######6   | 6606/10000 [00:13<00:23, 144.82it/s]\n",
      " 66%|######6   | 6639/10000 [00:13<00:22, 152.59it/s]\n",
      " 67%|######6   | 6669/10000 [00:14<00:32, 102.22it/s]\n",
      " 67%|######7   | 6729/10000 [00:14<00:22, 145.49it/s]\n",
      " 69%|######8   | 6856/10000 [00:14<00:11, 269.43it/s]\n",
      " 70%|######9   | 6992/10000 [00:14<00:07, 416.84it/s]\n",
      " 71%|#######1  | 7108/10000 [00:15<00:05, 537.58it/s]\n",
      " 73%|#######2  | 7256/10000 [00:15<00:03, 713.88it/s]\n",
      " 74%|#######4  | 7419/10000 [00:15<00:02, 906.09it/s]\n",
      " 76%|#######5  | 7583/10000 [00:15<00:02, 1074.10it/s]\n",
      " 78%|#######7  | 7761/10000 [00:15<00:01, 1246.57it/s]\n",
      " 80%|#######9  | 7999/10000 [00:15<00:01, 1540.37it/s]\n",
      " 83%|########2 | 8287/10000 [00:15<00:00, 1902.00it/s]\n",
      " 85%|########4 | 8496/10000 [00:16<00:02, 740.28it/s] \n",
      " 87%|########6 | 8651/10000 [00:16<00:02, 644.76it/s]\n",
      " 88%|########7 | 8774/10000 [00:16<00:02, 600.86it/s]\n",
      " 89%|########8 | 8874/10000 [00:17<00:01, 592.31it/s]\n",
      " 90%|########9 | 8961/10000 [00:17<00:01, 580.53it/s]\n",
      " 90%|######### | 9038/10000 [00:17<00:01, 551.93it/s]\n",
      " 91%|#########1| 9106/10000 [00:17<00:01, 537.59it/s]\n",
      " 92%|#########1| 9168/10000 [00:17<00:01, 523.78it/s]\n",
      " 92%|#########2| 9226/10000 [00:17<00:01, 519.49it/s]\n",
      " 93%|#########2| 9282/10000 [00:17<00:01, 517.12it/s]\n",
      " 93%|#########3| 9337/10000 [00:18<00:01, 521.73it/s]\n",
      " 94%|#########3| 9392/10000 [00:18<00:01, 500.22it/s]\n",
      " 95%|#########4| 9452/10000 [00:18<00:01, 524.10it/s]\n",
      " 95%|#########5| 9506/10000 [00:18<00:00, 524.22it/s]\n",
      " 96%|#########5| 9560/10000 [00:18<00:00, 503.89it/s]\n",
      " 96%|#########6| 9612/10000 [00:18<00:00, 481.57it/s]\n",
      " 97%|#########6| 9661/10000 [00:18<00:00, 447.90it/s]\n",
      " 97%|#########7| 9712/10000 [00:18<00:00, 463.01it/s]\n",
      " 98%|#########7| 9778/10000 [00:18<00:00, 515.20it/s]\n",
      " 98%|#########8| 9849/10000 [00:19<00:00, 568.00it/s]\n",
      " 99%|#########9| 9915/10000 [00:19<00:00, 592.60it/s]\n",
      "100%|#########9| 9986/10000 [00:19<00:00, 624.77it/s]\n",
      "100%|##########| 10000/10000 [00:19<00:00, 518.51it/s]\n"
     ]
    }
   ],
   "source": [
    "!pip install sacremoses #和jieba分词类似\n",
    "!pip install subword-nmt\n",
    "# # BPE分词\n",
    "!sh data_multi30k.sh wmt16 de en"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Lvk4q0-1N4x-"
   },
   "source": [
    "Dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "zCXKEwspN4x_",
    "outputId": "4a6a00cb-f0e2-4f44-d6e4-6a3d507e880b",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:27.195882500Z",
     "start_time": "2024-05-08T02:58:27.124921700Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "load train dataset from wmt16\\.cache\\de2en_train_128.npy\n",
      "load val dataset from wmt16\\.cache\\de2en_val_128.npy\n"
     ]
    }
   ],
   "source": [
    "from pathlib import Path\n",
    "from torch.utils.data import Dataset, DataLoader\n",
    "\n",
    "\n",
    "\n",
    "class LangPairDataset(Dataset):\n",
    "\n",
    "    def __init__(\n",
    "        self, mode=\"train\", max_length=128, overwrite_cache=False, data_dir=\"wmt16\",\n",
    "    ):\n",
    "        self.data_dir = Path(data_dir)\n",
    "        cache_path = self.data_dir / \".cache\" / f\"de2en_{mode}_{max_length}.npy\"\n",
    "\n",
    "        if overwrite_cache or not cache_path.exists():\n",
    "            cache_path.parent.mkdir(parents=True, exist_ok=True)\n",
    "\n",
    "            with open(self.data_dir / f\"{mode}_src.bpe\", \"r\", encoding=\"utf8\") as file:\n",
    "                self.src = file.readlines() # 读取源语言文件所有行\n",
    "\n",
    "            with open(self.data_dir / f\"{mode}_trg.bpe\", \"r\", encoding=\"utf8\") as file:\n",
    "                self.trg = file.readlines() # 读取目标语言文件所有行\n",
    "\n",
    "            filtered_src = []\n",
    "            filtered_trg = []\n",
    "            # max length filter,超出最大长度的句子舍弃\n",
    "            for src, trg in zip(self.src, self.trg):\n",
    "                if len(src) <= max_length and len(trg) <= max_length: # 过滤长度超过最大长度的句子\n",
    "                    filtered_src.append(src.strip()) # 去掉句子前后的空格\n",
    "                    filtered_trg.append(trg.strip())\n",
    "            filtered_src = np.array(filtered_src)\n",
    "            filtered_trg = np.array(filtered_trg)\n",
    "            np.save(\n",
    "                cache_path,\n",
    "                {\"src\": filtered_src, \"trg\": filtered_trg },\n",
    "                allow_pickle=True,\n",
    "            )#allow_pickle=True允许保存对象数组，将过滤后的数据保存为 NumPy 数组，存储在缓存文件中\n",
    "            print(f\"save cache to {cache_path}\")\n",
    "\n",
    "        else:\n",
    "            cache_dict = np.load(cache_path, allow_pickle=True).item() #allow_pickle=True允许保存对象数组\n",
    "            print(f\"load {mode} dataset from {cache_path}\")\n",
    "            filtered_src = cache_dict[\"src\"]\n",
    "            filtered_trg = cache_dict[\"trg\"]\n",
    "\n",
    "        self.src = filtered_src\n",
    "        self.trg = filtered_trg\n",
    "\n",
    "    def __getitem__(self, index):\n",
    "        return self.src[index], self.trg[index]\n",
    "\n",
    "    def __len__(self):\n",
    "        return len(self.src)\n",
    "\n",
    "\n",
    "train_ds = LangPairDataset(\"train\")\n",
    "val_ds = LangPairDataset(\"val\")"
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "# !rm wmt16/.cache -r"
   ],
   "metadata": {
    "id": "yHB9TDpDQlv2",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:27.197890200Z",
     "start_time": "2024-05-08T02:58:27.173895800Z"
    }
   },
   "execution_count": 18,
   "outputs": []
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "outputs": [
    {
     "data": {
      "text/plain": "27659"
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(train_ds)"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "meHHXL3MN4x_",
    "outputId": "9d5d8ab4-04be-42f5-892c-b48fba7c3000",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:27.274837900Z",
     "start_time": "2024-05-08T02:58:27.195882500Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "LZvJV37NN4x_",
    "outputId": "239705f3-693e-4c53-c0e5-772117ef97e7",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:27.307816800Z",
     "start_time": "2024-05-08T02:58:27.215869700Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "source: ein älterer mann sitzt mit einem jungen mit einem wagen vor einer fassade .\n",
      "target: an elderly man sits outside a storefront accompani@@ ed by a young boy with a cart .\n"
     ]
    }
   ],
   "source": [
    "print(\"source: {}\\ntarget: {}\".format(*train_ds[-1]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "TE8gRzYQN4yA"
   },
   "source": [
    "### Tokenizer\n",
    "\n",
    "这里有两种处理方式，分别对应着 encoder 和 decoder 的 word embedding 是否共享，这里实现共享的方案"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 66,
     "referenced_widgets": [
      "2c39e7db4e4d4ff9b0a1e9dfaf84203a",
      "44d3be9cd000455dac8a0f8276a44f0e",
      "8c382bf77f9c496db2701139d7cb5540",
      "84f10bc140954a29b67af155bab4152b",
      "0b244b586d5d410c882afe33731462bc",
      "941a63e0eeab48608360eba3ff031455",
      "fcba3fc7e93747cdb56c59ea495fe50e",
      "92cce96ece7d49a5bf92ab69d3bd488c",
      "534c330eaa1c46f99d8da7365bca9253",
      "0fc0bd90e8cf45a9bb1555f11084e6fe",
      "38d606358679482bb9b5ec8029b91cb1"
     ]
    },
    "id": "yAmlq_9YN4yA",
    "outputId": "16096888-27bf-49dd-c601-5285da04b383",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:27.461803900Z",
     "start_time": "2024-05-08T02:58:27.227862Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "  0%|          | 0/9714 [00:00<?, ?it/s]",
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "97d91b2013784d7488eb16faecda8201"
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "vocab_size: 9718\n"
     ]
    }
   ],
   "source": [
    "#载入词表，看下词表长度，词表就像英语字典,构建word2idx和idx2word\n",
    "word2idx = {\n",
    "    \"[PAD]\": 0,     # 填充 token\n",
    "    \"[BOS]\": 1,     # begin of sentence\n",
    "    \"[UNK]\": 2,     # 未知 token\n",
    "    \"[EOS]\": 3,     # end of sentence\n",
    "}\n",
    "idx2word = {value: key for key, value in word2idx.items()}\n",
    "index = len(idx2word)\n",
    "threshold = 1  # 出现次数低于此的token舍弃\n",
    "\n",
    "with open(\"wmt16/vocab\", \"r\", encoding=\"utf8\") as file:\n",
    "    for line in tqdm(file.readlines()):\n",
    "        token, counts = line.strip().split()\n",
    "        if int(counts) >= threshold:\n",
    "            word2idx[token] = index\n",
    "            idx2word[index] = token\n",
    "            index += 1\n",
    "\n",
    "vocab_size = len(word2idx)\n",
    "print(\"vocab_size: {}\".format(vocab_size))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "e_ed0jmJN4yA",
    "outputId": "ae7593f7-9a89-4888-d3e8-c3b16dfead29",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:27.463802Z",
     "start_time": "2024-05-08T02:58:27.328805800Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "raw text\n",
      "['hello', 'world']\n",
      "['tokenize', 'text', 'datas', 'with', 'batch']\n",
      "['this', 'is', 'a', 'test']\n",
      "indices\n",
      "tensor([   2, 4517,    3,    0,    0,    0])\n",
      "tensor([   2, 7167,    2,   22,    2,    3])\n",
      "tensor([ 425,   18,    5, 4493,    3,    0])\n",
      "decode text\n",
      "[UNK] world [EOS] [PAD] [PAD] [PAD]\n",
      "[UNK] text [UNK] with [UNK] [EOS]\n",
      "this is a test [EOS] [PAD]\n"
     ]
    }
   ],
   "source": [
    "class Tokenizer:\n",
    "    def __init__(self, word2idx, idx2word, max_length=128, pad_idx=0, bos_idx=1, eos_idx=3, unk_idx=2):\n",
    "        self.word2idx = word2idx\n",
    "        self.idx2word = idx2word\n",
    "        self.max_length = max_length\n",
    "        self.pad_idx = pad_idx\n",
    "        self.bos_idx = bos_idx\n",
    "        self.eos_idx = eos_idx\n",
    "        self.unk_idx = unk_idx\n",
    "\n",
    "    def encode(self, text_list, padding_first=False, add_bos=True, add_eos=True, return_mask=False):\n",
    "        \"\"\"如果padding_first == True，则padding加载前面，否则加载后面\"\"\"\n",
    "        max_length = min(self.max_length, add_eos + add_bos + max([len(text) for text in text_list]))\n",
    "        indices_list = []\n",
    "        for text in text_list:\n",
    "            indices = [self.word2idx.get(word, self.unk_idx) for word in text[:max_length - add_bos - add_eos]]\n",
    "            if add_bos:\n",
    "                indices = [self.bos_idx] + indices\n",
    "            if add_eos:\n",
    "                indices = indices + [self.eos_idx]\n",
    "            if padding_first:\n",
    "                indices = [self.pad_idx] * (max_length - len(indices)) + indices\n",
    "            else:\n",
    "                indices = indices + [self.pad_idx] * (max_length - len(indices))\n",
    "            indices_list.append(indices)\n",
    "        input_ids = torch.tensor(indices_list)\n",
    "        masks = (input_ids == self.pad_idx).to(dtype=torch.int64) # 为了方便损失计算，这里的mask为0的地方需要计算，为1的地方不需要计算\n",
    "        return input_ids if not return_mask else (input_ids, masks)\n",
    "\n",
    "\n",
    "    def decode(self, indices_list, remove_bos=True, remove_eos=True, remove_pad=True, split=False):\n",
    "        text_list = []\n",
    "        for indices in indices_list:\n",
    "            text = []\n",
    "            for index in indices:\n",
    "                word = self.idx2word.get(index, \"[UNK]\")\n",
    "                if remove_bos and word == \"[BOS]\":\n",
    "                    continue\n",
    "                if remove_eos and word == \"[EOS]\":\n",
    "                    break\n",
    "                if remove_pad and word == \"[PAD]\":\n",
    "                    break\n",
    "                text.append(word)\n",
    "            text_list.append(\" \".join(text) if not split else text)\n",
    "        return text_list\n",
    "\n",
    "\n",
    "tokenizer = Tokenizer(word2idx=word2idx, idx2word=idx2word)\n",
    "\n",
    "tokenizer.encode([[\"hello\"], [\"hello\", \"world\"]], add_bos=True, add_eos=False)\n",
    "raw_text = [\"hello world\".split(), \"tokenize text datas with batch\".split(), \"this is a test\".split()]\n",
    "indices = tokenizer.encode(raw_text, padding_first=False, add_bos=False, add_eos=True)\n",
    "decode_text = tokenizer.decode(indices.tolist(), remove_bos=False, remove_eos=False, remove_pad=False)\n",
    "print(\"raw text\")\n",
    "for raw in raw_text:\n",
    "    print(raw)\n",
    "print(\"indices\")\n",
    "for index in indices:\n",
    "    print(index)\n",
    "print(\"decode text\")\n",
    "for decode in decode_text:\n",
    "    print(decode)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "ftcEnnKxN4yB"
   },
   "source": [
    "### Transformer Batch Sampler\n",
    "\n",
    "> Sentence pairs were batched together by approximate sequence length. Each training batch contained a set of sentence pairs containing approximately 25000 source tokens and 25000 target tokens\n",
    "句子对按大致序列长度分批在一起。 每个训练批次包含一组句子对，其中包含大约 25000 个源标记和 25000 个目标标记"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "id": "qP8nznIuN4yB",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:27.464801600Z",
     "start_time": "2024-05-08T02:58:27.375778500Z"
    }
   },
   "outputs": [],
   "source": [
    "class SampleInfo: #下面的info对象\n",
    "    def __init__(self, i, lens):\n",
    "        \"\"\"\n",
    "        记录文本对的序号和长度信息\n",
    "        输入：\n",
    "            - i (int): 文本对的序号。\n",
    "            - lens (list): 文本对源语言和目标语言的长度\n",
    "        \"\"\"\n",
    "        self.i = i\n",
    "        # 加一是考虑填补在文本前后的特殊词元\n",
    "        self.max_len = max(lens[0], lens[1]) + 1\n",
    "        self.src_len = lens[0] + 1\n",
    "        self.trg_len = lens[1] + 1\n",
    "\n",
    "# 一个批量生成器，根据词元数目的限制来控制批量的大小。它会根据传入的样本信息，在不超过设定大小的情况下，逐步构建批量。\n",
    "class TokenBatchCreator:\n",
    "    def __init__(self, batch_size):\n",
    "        \"\"\"\n",
    "        参数:\n",
    "        batch_size (int): 用于限制批量的大小。\n",
    "        功能:\n",
    "        初始化了一个空的批量列表 _batch。\n",
    "        设定了初始的最大长度为 -1。\n",
    "        存储了传入的 batch_size。\n",
    "        \"\"\"\n",
    "\n",
    "        self._batch = []\n",
    "        self.max_len = -1\n",
    "        self._batch_size = batch_size # 限制批量的大小,假设是4096\n",
    "\n",
    "    def append(self, info):\n",
    "        \"\"\"\n",
    "        参数:\n",
    "        info (SampleInfo): 文本对的信息。\n",
    "        功能:\n",
    "        接收一个 SampleInfo 对象，并根据其最大长度信息更新当前批量的最大长度。\n",
    "        如果将新的样本加入批量后超过了批量大小限制，它会返回已有的批量并将新的样本加入新的批量。\n",
    "        否则，它会更新最大长度并将样本添加到当前批量中。\n",
    "        \"\"\"\n",
    "        # 更新当前批量的最大长度\n",
    "        cur_len = info.max_len # 当前样本的长度\n",
    "        max_len = max(self.max_len, cur_len) # 每来一个样本，更新当前批次的最大长度\n",
    "        # 如果新的样本加入批量后超过大小限制，则将已有的批量返回，新的样本加入新的批量\n",
    "        if max_len * (len(self._batch) + 1) > self._batch_size:\n",
    "            self._batch, result = [], self._batch # 保存当前的batch，并返回,这里的result是之前的batch,_batch清空\n",
    "            self._batch.append(info)\n",
    "            self.max_len = cur_len #因为是当前batch的第一个样本，所以它的长度就是当前长度\n",
    "            return result\n",
    "        else:\n",
    "            self.max_len = max_len\n",
    "            self._batch.append(info) # 将样本添加到当前批量中\n",
    "\n",
    "    @property\n",
    "    def batch(self):\n",
    "        return self._batch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {
    "id": "_Vtc0gXEN4yB",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:27.511777400Z",
     "start_time": "2024-05-08T02:58:27.405761500Z"
    }
   },
   "outputs": [],
   "source": [
    "from torch.utils.data import BatchSampler\n",
    "import numpy as np\n",
    "\n",
    "\n",
    "class TransformerBatchSampler(BatchSampler):\n",
    "    def __init__(self,\n",
    "                 dataset,\n",
    "                 batch_size,\n",
    "                 shuffle_batch=False,\n",
    "                 clip_last_batch=False,\n",
    "                 seed=0):\n",
    "        \"\"\"\n",
    "        批量采样器\n",
    "        输入:\n",
    "            - dataset: 数据集\n",
    "            - batch_size: 批量大小\n",
    "            - shuffle_batch: 是否对生成的批量进行洗牌\n",
    "            - clip_last_batch: 是否裁剪最后剩下的数据\n",
    "            - seed: 随机数种子\n",
    "        \"\"\"\n",
    "        self._dataset = dataset\n",
    "        self._batch_size = batch_size\n",
    "        self._shuffle_batch = shuffle_batch\n",
    "        self._clip_last_batch = clip_last_batch\n",
    "        self._seed = seed\n",
    "        self._random = np.random\n",
    "        self._random.seed(seed)\n",
    "\n",
    "        self._sample_infos = []\n",
    "        # 根据数据集中的每个样本，创建了对应的 SampleInfo 对象，包含了样本的索引和长度信息。\n",
    "        for i, data in enumerate(self._dataset):\n",
    "            lens = [len(data[0]), len(data[1])] #输入和输出的长度计算放到lens中\n",
    "            self._sample_infos.append(SampleInfo(i, lens))\n",
    "\n",
    "    def __iter__(self):\n",
    "        \"\"\"\n",
    "        对数据集中的样本进行排序，排序规则是先按源语言长度排序，如果相同则按目标语言长度排序。\n",
    "        使用 TokenBatchCreator 逐步组装批量数据，当满足批量大小时返回一个批量的样本信息。\n",
    "        如果不裁剪最后一个批次的数据且存在剩余样本，则将这些样本组成最后一个批次。\n",
    "        如果需要对批量进行洗牌，则对批次进行洗牌操作。\n",
    "        通过迭代器，抛出每个批量的样本在数据集中的索引。\n",
    "        \"\"\"\n",
    "        # 排序，如果源语言长度相同则按照目标语言的长度排列\n",
    "        infos = sorted(self._sample_infos,\n",
    "                       key=lambda x: (x.src_len, x.trg_len))\n",
    "        # 组装批量\n",
    "        batch_infos = []\n",
    "        batch_creator = TokenBatchCreator(self._batch_size) # 批量生成器\n",
    "        for info in infos:\n",
    "            batch = batch_creator.append(info)\n",
    "            # 存够一个batch的样本信息后，会把这个batch返回，否则返回为None\n",
    "            if batch is not None:\n",
    "                batch_infos.append(batch)\n",
    "\n",
    "        # 是否抛弃最后批量的文本对\n",
    "        if not self._clip_last_batch and len(batch_creator.batch) != 0:\n",
    "            batch_infos.append(batch_creator.batch) # 最后一个batch\n",
    "\n",
    "        # 打乱batch\n",
    "        if self._shuffle_batch:\n",
    "            self._random.shuffle(batch_infos)\n",
    "\n",
    "        self.batch_number = len(batch_infos)\n",
    "\n",
    "        # 抛出一个批量的文本对在数据集中的序号\n",
    "        for batch in batch_infos:\n",
    "            batch_indices = [info.i for info in batch] # 批量的样本在数据集中的索引，第一个batch[0,1,.....82]，第二个batch[83,84,85,86,87]\n",
    "            yield batch_indices\n",
    "\n",
    "    def __len__(self):\n",
    "        \"\"\"\n",
    "        返回批量的数量\n",
    "        \"\"\"\n",
    "        if hasattr(self, \"batch_number\"):\n",
    "            return self.batch_number\n",
    "        # 计算批量的数量\n",
    "        batch_number = (len(self._dataset) +\n",
    "                        self._batch_size) // self._batch_size\n",
    "        return batch_number"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "d00AFRzVN4yC",
    "outputId": "1a01ea09-91aa-4277-f1b0-81d925365192",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:27.882566200Z",
     "start_time": "2024-05-08T02:58:27.432746700Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第0批量的数据中含有文本对是：[20303, 272, 3549, 11130, 12595, 12784, 13559, 22464, 1474, 6750, 7174, 8451, 10507, 15759, 16727, 19970, 25362, 27342, 27467, 2093, 2953, 3053, 4811, 6859, 10441, 10853, 11970, 15839, 16938, 18233, 20382, 21262, 24568, 14, 1067, 4934, 10278, 11027, 11753, 12988, 15029, 15900, 16219, 16799, 19208, 20018, 21926, 2490, 2565, 2854, 3903, 6469, 8006, 9892, 10241, 10400, 11697, 13678, 17015, 19819, 20660, 23208, 25640, 4620, 5719, 7807, 9474, 11360, 11423, 11857, 12519, 14495, 16832, 17204, 17214, 22914, 23865, 25037, 26610, 27438, 365, 1063, 3404]，数量为：83\n",
      "第1批量的数据中含有文本对是：[1896, 2650, 5347, 6855, 13019, 14422, 15533, 17704, 20935, 21794, 27107, 2422, 5213, 6555, 6638, 7682, 7902, 14492, 19817, 20186, 20941, 21052, 21164, 21222, 23422, 23573, 23834, 24516, 24599, 528, 933, 5364, 7649, 8029, 12179, 16073, 17508, 17732, 20361, 21449, 25890, 26193, 26438, 1469, 5758, 9238, 10232, 16510, 17373]，数量为：49\n",
      "第2批量的数据中含有文本对是：[6311, 19570, 21049, 21560, 24196, 24600, 12012, 12977, 23611, 24911, 26514, 2573, 6205, 8269, 22566, 23845, 24888, 16511, 13479, 14827, 24583, 11595, 13740, 16399, 23925, 3068, 4682, 6643, 21833, 51, 2894, 4777, 6227, 10547, 20315]，数量为：35\n",
      "第3批量的数据中含有文本对是：[12483, 18790, 19995, 22195, 27532, 8260, 11850, 15860, 21670, 8611, 15832, 19348, 21688, 3206, 20749, 27041, 27254, 9762, 12951, 19090, 19566, 5503, 6830, 9750, 12289, 18415, 19626, 20953, 23116, 252, 1881, 3617, 13355, 13982, 16612, 26396]，数量为：36\n"
     ]
    }
   ],
   "source": [
    "sampler = TransformerBatchSampler(train_ds, batch_size=4096, shuffle_batch=True)\n",
    "for idx, batch in enumerate(sampler):\n",
    "    print(\"第{}批量的数据中含有文本对是：{}，数量为：{}\".format(idx, batch, len(batch)))\n",
    "    if idx >= 3:\n",
    "        break\n",
    "#为什么这里每个批量的样本对数目不一样呢？长度*batch_number>4096的时候，就会返回上一个batch，然后新的样本加入新的batch,具体要看TokenBatchCreator的44行"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "Olkaw4JNN4yC"
   },
   "source": [
    "### DataLoader"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {
    "id": "rvvuNJIzN4yC",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:27.952520900Z",
     "start_time": "2024-05-08T02:58:27.893562200Z"
    }
   },
   "outputs": [],
   "source": [
    "def collate_fct(batch, tokenizer):\n",
    "    src_words = [pair[0].split() for pair in batch]\n",
    "    trg_words = [pair[1].split() for pair in batch]\n",
    "\n",
    "    # [BOS] src [EOS] [PAD]\n",
    "    encoder_inputs, encoder_inputs_mask = tokenizer.encode(\n",
    "        src_words, padding_first=False, add_bos=True, add_eos=True, return_mask=True\n",
    "        )\n",
    "\n",
    "    # [BOS] trg [PAD]\n",
    "    decoder_inputs = tokenizer.encode(\n",
    "        trg_words, padding_first=False, add_bos=True, add_eos=False, return_mask=False,\n",
    "        )\n",
    "\n",
    "    # trg [EOS] [PAD]\n",
    "    decoder_labels, decoder_labels_mask = tokenizer.encode(\n",
    "        trg_words, padding_first=False, add_bos=False, add_eos=True, return_mask=True\n",
    "        )\n",
    "\n",
    "    return {\n",
    "        \"encoder_inputs\": encoder_inputs.to(device=device),\n",
    "        \"encoder_inputs_mask\": encoder_inputs_mask.to(device=device),\n",
    "        \"decoder_inputs\": decoder_inputs.to(device=device),\n",
    "        \"decoder_labels\": decoder_labels.to(device=device),\n",
    "        \"decoder_labels_mask\": decoder_labels_mask.to(device=device),\n",
    "    }\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "5p79gPo5N4yC",
    "outputId": "aab22f44-b5ae-42c6-a5b6-8cbaa3794d77",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:28.161983800Z",
     "start_time": "2024-05-08T02:58:27.915544100Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "encoder_inputs\n",
      "tensor([[   1,    7,   17,    6,    8,   89,  157,   73,    7,  541,  255,    9,\n",
      "           14,   42,  153,  119, 1481,  406, 2384, 1319,    4,    3],\n",
      "        [   1,  521,    6,    8,  808, 1299, 2305, 1638, 2535,  184,   31, 1735,\n",
      "          961,   55, 1732,    4,    3,    0,    0,    0,    0,    0]])\n",
      "encoder_inputs_mask\n",
      "tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1]])\n",
      "decoder_inputs\n",
      "tensor([[   1,    5,   16,    6,    5,   50,   41,   18,   78,    5,  421,  254,\n",
      "         7050,  106,  471,    4],\n",
      "        [   1,  452,    6,    5, 5010, 1118, 1010,  106,    5,  517, 5103,    4,\n",
      "            0,    0,    0,    0]])\n",
      "decoder_labels\n",
      "tensor([[   5,   16,    6,    5,   50,   41,   18,   78,    5,  421,  254, 7050,\n",
      "          106,  471,    4,    3],\n",
      "        [ 452,    6,    5, 5010, 1118, 1010,  106,    5,  517, 5103,    4,    3,\n",
      "            0,    0,    0,    0]])\n",
      "decoder_labels_mask\n",
      "tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],\n",
      "        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]])\n"
     ]
    }
   ],
   "source": [
    "from functools import partial # 固定collate_fct的tokenizer参数\n",
    "\n",
    "\n",
    "sampler = TransformerBatchSampler(train_ds, batch_size=256, shuffle_batch=True)\n",
    "# https://pytorch.org/docs/stable/data.html#torch.utils.data.DataLoader\n",
    "sample_dl = DataLoader(train_ds, batch_sampler=sampler, collate_fn=partial(collate_fct, tokenizer=tokenizer)) #partial函数，固定collate_fct的tokenizer参数\n",
    "\n",
    "for batch in sample_dl:\n",
    "    for key, value in batch.items():\n",
    "        print(key)\n",
    "        print(value)\n",
    "    break"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "0AadtoM3N4yC"
   },
   "source": [
    "## 定义模型\n",
    "\n",
    "- Transformer模型由Embedding、Transformer-Block组成\n",
    "- Embedding包括：\n",
    "    - WordEmbedding\n",
    "    - PositionEmbedding\n",
    "- Transformer-Block包括：\n",
    "    - Self-Attention\n",
    "    - Cross-Attention\n",
    "    - MLP"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "VTl-sSJmN4yD"
   },
   "source": [
    "### Embedding"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 455
    },
    "id": "y66CxrsBN4yD",
    "outputId": "9978188d-45d8-4bd3-fd90-606d3814bfe1",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:28.516432600Z",
     "start_time": "2024-05-08T02:58:28.171984Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "<Figure size 640x480 with 2 Axes>",
      "image/png": "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ls81cq1JjrzU1NSgoKDAqTS3Xq9HRkYG8vPzfXhmjev3SpQ3FecWSYqJiQEAFBQUoLa21um6u3TpgjZt2jSZ67bZbFi5ciUqKyuRnp4eFNc8ZcoUjBgxwukagab9+96zZw8SExPRrl07jB07FgcPHgTQtK/5nXfeQb9+/XD99dejZcuW6NOnD1577TXH88HyuUauNcnOy9GjR2Gz2c5bhTcuLg7FxcU+OqvGd+5am/LrYLfbMXXqVAwcOBDdu3cHcPa6zWYzoqKinNo2hev+5ptvEBYWBovFgttvvx1r1qxBt27dmvQ1A8DKlSuxY8cOzJ8//7znmuq1p6WlYdmyZcjNzcXLL7+M/fv349JLL8WpU6ea7DUDwE8//YSXX34ZHTt2xAcffIA77rgDd999t2O112D4XKPf5/flAYhcmTJlCr799lunuQBNWefOnbFz506cPHkSb731FsaPH4/Nmzf7+rQaVGFhIe655x5s2LCh0avw+tKVV17p+P+ePXsiLS0Nbdu2xb/+9S+EhIT48Mwalt1uR79+/fDYY48BOLss/bfffotFixZh/PjxPj478hdNcuSlRYsWMBgM5828LykpcbvsdiD7dYnyX2sqr0NWVhbeffddfPzxx2jdurUjHh8fj5qaGpSVlTm1bwrXbTab0aFDB6SmpmL+/Pno1asXnn/++SZ9zQUFBSgtLUXfvn1hNBphNBqxefNmLFy4EEajEXFxcU322n8tKioKnTp1wt69e5v07zshIQHdunVzinXt2tVxy6ypf67RhWmSnRez2YzU1FTk5eU5Yna7HXl5eUhPT/fhmTWuX5coP+dcifJAfh00TUNWVhbWrFmDjRs3IiUlxen51NRUmEwmp+vevXs3Dh48GNDXLbHb7aiurm7S1zx06FB88803jqq2O3fuRL9+/TB27FjH/zfVa/+1iooK7Nu3DwkJCU369z1w4MDzlj748ccf0bZtWwBN93ON3OTrGcMNZeXKlZrFYtGWLVumff/999rkyZO1qKgorbi42NenVq9OnTqlffnll9qXX36pAdCeffZZ7csvv9R+/vlnTdM07fHHH9eioqK0t99+W/v666+1kSNHaikpKdqZM2d8fOaeu+OOO7TIyEht06ZN2uHDhx2P06dPO9rcfvvtWps2bbSNGzdq27dv19LT07X09HQfnrX3HnzwQW3z5s3a/v37ta+//lp78MEHNZ1Op3344YeapjXNa1b5dbaRpjXNa7/33nu1TZs2afv379c+++wzLSMjQ2vRooVWWlqqaVrTvGZN07Rt27ZpRqNRe/TRR7U9e/Zob775phYaGqr94x//cLRpip9r5J4m23nRNE174YUXtDZt2mhms1nr37+/tmXLFl+fUr37+OOPNQDnPcaPH69p2tm0wocffliLi4vTLBaLNnToUG337t2+PWkvSdcLQFu6dKmjzZkzZ7Q777xTi46O1kJDQ7Vrr71WO3z4sO9Ouh785S9/0dq2bauZzWYtNjZWGzp0qKPjomlN85pVftt5aYrXfuONN2oJCQma2WzWWrVqpd14443a3r17Hc83xWs+Z926dVr37t01i8WidenSRXv11Vednm+Kn2vkHp2maZpvxnyIiIiI3Nck57wQERFR08XOCxEREQUUdl6IiIgooLDzQkRERAGFnRciIiIKKOy8EBERUUBh54WIiIgCCjsvRHRBDhw4AJ1Oh507d/r6VIgoyLHzQhQgbr31Vuh0Ouh0OphMJsTFxeGKK67AkiVLYLfb6/1Yo0aNqtd9EhHVF3ZeiALI8OHDcfjwYRw4cADr16/H5ZdfjnvuuQdXX3016urqfH16RESNgp0XogBisVgQHx+PVq1aoW/fvvjb3/6Gt99+G+vXr8eyZcsAAGVlZbjtttsQGxuLiIgIDBkyBF999ZVjH7Nnz0bv3r3xyiuvICkpCaGhobjhhhtw8uRJx/NvvPEG3n77bcdIz6ZNmxzb//TTT7j88ssRGhqKXr16IT8/vzFfAiIidl6IAt2QIUPQq1cv/Oc//wEAXH/99SgtLcX69etRUFCAvn37YujQoTh+/Lhjm7179+Jf//oX1q1bh9zcXHz55Ze48847AQD33XcfbrjhBscoz+HDhzFgwADHtn//+99x3333YefOnejUqRNuuukmjvoQUaNi54WoCejSpQsOHDiATz/9FNu2bcPq1avRr18/dOzYEU8//TSioqLw1ltvOdpXVVVh+fLl6N27Ny677DK88MILWLlyJYqLixEWFoaQkBDHKE98fDzMZrNj2/vuuw8jRoxAp06dMGfOHPz888/Yu3evLy6biIIUOy9ETYCmadDpdPjqq69QUVGB5s2bIywszPHYv38/9u3b52jfpk0btGrVyvFzeno67HY7du/e/bvH6tmzp+P/ExISAAClpaX1eDVERK4ZfX0CROS9Xbt2ISUlBRUVFUhISHCao3JOVFRUvRzLZDI5/l+n0wFAvWc7ERG5ws4LUYDbuHEjvvnmG0ybNg2tW7dGcXExjEYjkpOTldscPHgQRUVFSExMBABs2bIFer0enTt3BgCYzWbYbLbGOH0iIrex80IUQKqrq1FcXAybzYaSkhLk5uZi/vz5uPrqqzFu3Djo9Xqkp6dj1KhRePLJJ9GpUycUFRXhvffew7XXXot+/foBAKxWK8aPH4+nn34a5eXluPvuu3HDDTcgPj4eAJCcnIwPPvgAu3fvRvPmzREZGenLyyYicsLOC1EAyc3NRUJCAoxGI6Kjo9GrVy8sXLgQ48ePh15/dgrb+++/j7///e+YMGECjhw5gvj4eFx22WWIi4tz7KdDhw647rrrcNVVV+H48eO4+uqr8dJLLzmenzRpEjZt2oR+/fqhoqICH3/8scuRHCKixqTTNE3z9UkQUeOZPXs21q5dy2X+iShgMduIiIiIAgo7L0RERBRQeNuIiIiIAgpHXoiIiCigsPNCREREAYWdFyIiIgoo7LwQERFRQGHnhYiIiAIKOy9EREQUUNh5ISIiooDCzgsREREFFHZeiIiIKKD8P07CaPSrcdxSAAAAAElFTkSuQmCC"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\n",
    "class TransformerEmbedding(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.vocab_size = config[\"vocab_size\"]\n",
    "        self.hidden_size = config[\"d_model\"]\n",
    "        self.pad_idx = config[\"pad_idx\"]\n",
    "        dropout_rate = config[\"dropout\"]\n",
    "        self.max_length = config[\"max_length\"]\n",
    "\n",
    "        # layers\n",
    "        self.word_embedding = nn.Embedding(\n",
    "            self.vocab_size, self.hidden_size, padding_idx=self.pad_idx\n",
    "        )\n",
    "        self.pos_embedding = nn.Embedding(\n",
    "            self.max_length,\n",
    "            self.hidden_size,\n",
    "            _weight=self.get_positional_encoding(\n",
    "                self.max_length, self.hidden_size\n",
    "            ),# 位置编码，权重通过get_positional_encoding函数计算得到\n",
    "        )\n",
    "        self.pos_embedding.weight.requires_grad_(False) # 不更新位置编码的权重\n",
    "        self.dropout = nn.Dropout(dropout_rate) # 随机失活层\n",
    "\n",
    "    def get_word_embedding_weights(self):\n",
    "        return self.word_embedding.weight\n",
    "\n",
    "    # 计算位置信息\n",
    "    @classmethod\n",
    "    def get_positional_encoding(self, max_length, hidden_size):#max_length是最大长度，hidden_size是embedding维度相等\n",
    "        # Compute the positional encodings once in log space.\n",
    "        pe = torch.zeros(max_length, hidden_size) # 初始化位置编码\n",
    "        # .unsqueeze(1) 是将这个一维张量转换为二维张量，即将其形状从 (max_length,) 变为 (max_length, 1)。这个操作在张量的维度上增加了一个维度，使其从一维变为二维，第二维的大小为 1。\n",
    "        position = torch.arange(0, max_length).unsqueeze(1) # 位置信息,从0到max_length-1\n",
    "        div_term = torch.exp(\n",
    "            torch.arange(0, hidden_size, 2)\n",
    "            * -(torch.log(torch.Tensor([10000.0])) / hidden_size)\n",
    "        )# 计算位置编码的权重,为了性能考量（是数学上的对数函数分解）\n",
    "        pe[:, 0::2] = torch.sin(position * div_term)\n",
    "        pe[:, 1::2] = torch.cos(position * div_term)\n",
    "        return pe\n",
    "\n",
    "    def forward(self, input_ids):\n",
    "        # input_ids: [batch_size, seq_len]\n",
    "        seq_len = input_ids.shape[1]\n",
    "        assert (\n",
    "            seq_len <= self.max_length\n",
    "        ), f\"input sequence length should no more than {self.max_length} but got {seq_len}\"\n",
    "\n",
    "        position_ids = torch.arange(seq_len, dtype=torch.long, device=input_ids.device)\n",
    "        position_ids = position_ids.unsqueeze(0).expand_as(input_ids)\n",
    "\n",
    "        # embedding\n",
    "        word_embeds = self.word_embedding(input_ids) # 词嵌入\n",
    "        pos_embeds = self.pos_embedding(position_ids) # 位置编码\n",
    "        embeds = word_embeds + pos_embeds\n",
    "        embeds = self.dropout(embeds)\n",
    "\n",
    "        return embeds\n",
    "\n",
    "\n",
    "def plot_position_embedding(position_embedding):# 绘制位置编码\n",
    "    plt.pcolormesh(position_embedding) # 绘制位置编码矩阵\n",
    "    plt.xlabel('Depth')\n",
    "    plt.ylabel('Position')\n",
    "    plt.colorbar() # 颜色条，-1到1的颜色范围\n",
    "    plt.show()\n",
    "\n",
    "position_embedding = TransformerEmbedding.get_positional_encoding(64, 64)\n",
    "plot_position_embedding(position_embedding)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "wXavO0SNN4yD"
   },
   "source": [
    "### Transformer Block"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "HZL4mu3pN4yD"
   },
   "source": [
    "#### scaled-dot-product-attention"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 901
    },
    "id": "U9QwdVQYN4yD",
    "outputId": "643e18e8-32ce-44e6-c836-dec8d4e84855",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:30.731913900Z",
     "start_time": "2024-05-08T02:58:28.571400400Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "key_value.shape torch.Size([2, 4, 2])\n",
      "torch.Size([2, 3, 2])\n",
      "torch.Size([2, 2, 3, 4])\n"
     ]
    },
    {
     "data": {
      "text/plain": "<Figure size 640x480 with 4 Axes>",
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------\n"
     ]
    },
    {
     "data": {
      "text/plain": "<Figure size 640x480 with 4 Axes>",
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhUAAAG6CAYAAAC/RrTYAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAACL5UlEQVR4nOzdd3gU9dbA8e+W9N5DIPQaOgkJoTfpqIAC1gAqFtCLXMuLDbBhVxTsCnYRlCJIl947BBJqQkJCeu9l5/0jsGHJBhLYArnn8zz7QGZ/M3vOzszJ2SkblaIoCkIIIYQQN0lt7QCEEEIIUTdIUyGEEEIIk5CmQgghhBAmIU2FEEIIIUxCmgohhBBCmIQ0FUIIIYQwCWkqhBBCCGES0lQIIYQQwiSkqRBCCCGESUhTIW5JEyZMoHHjxjUaO2vWLFQq1XXH9e3bl3bt2t1kZKbVuHFjJkyYYO0wLKY26/VWUNNt68qxaWlpZo7K/CZMmICzs7O1wxC3IWkqxG2hoKCAWbNmsXnzZmuHckt7++23WbZsWZXpO3fuZNasWWRlZZk9hsTERGbNmsXhw4fN/lrWUN17LISQpkLcJgoKCpg9e7bRpuKVV16hsLDQ8kHdgq7VVMyePdtiTcXs2bONNhXffPMNJ0+eNHsMpmJs25KmQojqaa0dgBA3S6vVotXKpnw7sLGxsXYItSLblhC1I0cqRI1dPmd86tQpHnzwQdzc3PDx8eHVV19FURTi4+O56667cHV1xd/fnw8//NBg/oULF6JSqYiNjTWYvnnzZlQqVbWnNmJjY/Hx8QFg9uzZqFQqVCoVs2bNMoirpk6cOEG/fv1wdHSkfv36vPfee1XGFBcXM3PmTJo3b46dnR2BgYG88MILFBcXG4xbsGAB/fv3x9fXFzs7O4KCgvjiiy+qLE9RFN58800aNGiAo6Mj/fr14/jx4zWO+YMPPqB79+54eXnh4OBAcHAwS5YsMRijUqnIz8/nhx9+0L9HEyZMYNasWTz//PMANGnSRP/clevh559/Jjg4GAcHBzw9PRk/fjzx8fEGy798Tcq13r/NmzfTtWtXACZOnKh/rYULFwLGr6nIz8/nv//9L4GBgdjZ2dGqVSs++OADrv4DyiqViqlTp7Js2TLatWuHnZ0dbdu2Zc2aNdd87xRFwdvbm+nTp+un6XQ63N3d0Wg0Bkdv3n33XbRaLXl5eUDVbau69/hKWVlZTJgwAXd3d9zc3Jg4cSIFBQXXjBEq39+jR4/Sp08fHB0dad68uX49b9myhbCwMBwcHGjVqhUbNmwwmP/8+fM89dRTtGrVCgcHB7y8vLj33nur7G+lpaXMnj2bFi1aYG9vj5eXFz179mT9+vXXjO/w4cP4+PjQt29f/fsjxNWkqRC1Nm7cOHQ6He+88w5hYWG8+eabfPLJJ9xxxx3Ur1+fd999l+bNm/Pcc8+xdevWm349Hx8f/S/qUaNG8dNPP/HTTz8xevToWi8rMzOTIUOG0LFjRz788ENat27Niy++yOrVq/VjdDodd955Jx988AEjR47ks88+4+677+bjjz9m3LhxBsv74osvaNSoES+99BIffvghgYGBPPXUU8yfP99g3Guvvcarr75Kx44def/992natCmDBg0iPz+/RnHPnTuXzp078/rrr/P222+j1Wq59957WbVqlX7MTz/9hJ2dHb169dK/R48//jijR4/mvvvuA+Djjz/WP3e5UXvrrbd4+OGHadGiBR999BHTpk1j48aN9O7du8rpkuu9f23atOH1118HYPLkyfrX6t27t9G8FEXhzjvv5OOPP2bIkCF89NFHtGrViueff96gCbhs+/btPPXUU4wfP5733nuPoqIixowZQ3p6erXvnUqlokePHgbb4tGjR8nOzgZgx44d+unbtm2jc+fO1V6kWN17fKWxY8eSm5vLnDlzGDt2LAsXLmT27NnVxnelzMxMRowYQVhYGO+99x52dnaMHz+eRYsWMX78eIYNG8Y777xDfn4+99xzD7m5ufp59+3bx86dOxk/fjyffvopTzzxBBs3bqRv374GTc2sWbOYPXs2/fr1Y968ebz88ss0bNiQgwcPVhvXvn376N+/P507d2b16tVyEaeoniJEDc2cOVMBlMmTJ+unlZWVKQ0aNFBUKpXyzjvv6KdnZmYqDg4OSkREhH7aggULFECJiYkxWO6mTZsUQNm0aZN+WkREhNKoUSP9z6mpqQqgzJw5s9q4rqdPnz4KoPz444/6acXFxYq/v78yZswY/bSffvpJUavVyrZt2wzm//LLLxVA2bFjh35aQUFBldcZPHiw0rRpU/3PKSkpiq2trTJ8+HBFp9Ppp7/00ksKYPAeVefq1ykpKVHatWun9O/f32C6k5OT0eW9//77Rt/72NhYRaPRKG+99ZbB9GPHjilardZgek3fv3379imAsmDBgipxXL1ely1bpgDKm2++aTDunnvuUVQqlXLmzBn9NECxtbU1mHbkyBEFUD777LMqr3V1/hqNRsnJyVEURVE+/fRTpVGjRkpoaKjy4osvKoqiKOXl5Yq7u7vy7LPP6ucztm1V9x5fHjtp0iSD6aNGjVK8vLyuGZ+iVL6/v/76q35adHS0AihqtVrZvXu3fvratWurvMfGtsVdu3ZVWWcdO3ZUhg8ffs1YIiIiFCcnJ0VRFGX79u2Kq6urMnz4cKWoqOi6eYj/bXKkQtTao48+qv+/RqMhJCQERVF45JFH9NPd3d1p1aoV586ds0aI1XJ2dubBBx/U/2xra0toaKhBnIsXL6ZNmza0bt2atLQ0/aN///4AbNq0ST/WwcFB///s7GzS0tLo06cP586d038S3rBhAyUlJTz99NMGh9KnTZtW47ivfJ3MzEyys7Pp1avXNT9d1sRff/2FTqdj7NixBrn6+/vTokULg1yhZu9fbfzzzz9oNBqeeeYZg+n//e9/URTF4AgSwMCBA2nWrJn+5w4dOuDq6nrd1+/Vqxfl5eXs3LkTqDgi0atXL3r16sW2bdsAiIyMJCsri169et1QLpc98cQTVV47PT2dnJyc687r7OzM+PHj9T+3atUKd3d32rRpQ1hYmH765f9fmfeV20hpaSnp6ek0b94cd3d3g+3E3d2d48ePc/r06evGs2nTJgYPHsyAAQP466+/sLOzu+484n+bNBWi1ho2bGjws5ubG/b29nh7e1eZnpmZacnQrqtBgwZVrr/w8PAwiPP06dMcP34cHx8fg0fLli0BSElJ0Y/dsWMHAwcOxMnJCXd3d3x8fHjppZcA9E3F+fPnAWjRooXB6/r4+ODh4VGjuFeuXEm3bt2wt7fH09NTf0ro8mvcqNOnT6MoCi1atKiSb1RUlEGuULP3rzbOnz9PQEAALi4uBtPbtGmjf/5KV297NX39Ll264OjoqG8gLjcVvXv3Zv/+/RQVFemf69mz5w3lUl2Ml9dxTd4jY++vm5sbgYGBVaZdvczCwkJee+01/bUp3t7e+Pj4kJWVZbCdvP7662RlZdGyZUvat2/P888/z9GjR6vEUlRUxPDhw+ncuTN//PEHtra2141fCLmsWdSaRqOp0TTA4GK76i6mLC8vN01gNVCTOHU6He3bt+ejjz4yOvZygT979iwDBgygdevWfPTRRwQGBmJra8s///zDxx9/jE6nM0nM27Zt484776R37958/vnn1KtXDxsbGxYsWMCvv/56U8vW6XSoVCpWr15t9L25+tx5Td4/c7rR17exsSEsLIytW7dy5swZkpKS6NWrF35+fpSWlrJnzx62bdtG69at9deaWDrGa81bk2U+/fTTLFiwgGnTphEeHo6bmxsqlYrx48cbbIu9e/fm7NmzLF++nHXr1vHtt9/y8ccf8+WXXxochbSzs2PYsGEsX76cNWvWMGLEiOvGL4Q0FcJiLn9iu/riv6s/jRpTm7s7blazZs04cuQIAwYMuObr/v333xQXF7NixQqDT6dXnzJo1KgRUHFUoGnTpvrpqampNfr0+ueff2Jvb8/atWsNDj8vWLCgytjq4q1uerNmzVAUhSZNmuiPxNys2qyrRo0asWHDBnJzcw2OVkRHR+ufN5VevXrx7rvvsmHDBry9vWndujUqlYq2bduybds2tm3bVqNfnJbcFmtjyZIlREREGNx1VVRUZPS7STw9PZk4cSITJ04kLy+P3r17M2vWLIOmQqVS8csvv3DXXXdx7733snr1avr27WuBTMTtTE5/CIu5fC78yqvwy8vL+frrr687r6OjI1C1ITGHsWPHkpCQwDfffFPlucLCQv0dG5c/PV75aTE7O7vKL/uBAwdiY2PDZ599ZjD2k08+qVE8Go0GlUplcEQnNjbW6BcwOTk5GX2PnJycgKrv3+jRo9FoNMyePbvKJ2lFUa55V0V1qnstY4YNG0Z5eTnz5s0zmP7xxx+jUqkYOnRorV+/Or169aK4uJhPPvmEnj176puDy3dyJCYm1uh6iureY2vTaDRV1uFnn31W5Ujg1evU2dmZ5s2bV7ldGiqumfnrr7/o2rUrI0eOZO/evaYPXNQpcqRCWEzbtm3p1q0bM2bMICMjA09PT37//XfKysquO6+DgwNBQUEsWrSIli1b4unpSbt27czytzweeugh/vjjD5544gk2bdpEjx49KC8vJzo6mj/++IO1a9cSEhLCoEGDsLW1ZeTIkTz++OPk5eXxzTff4Ovry8WLF/XL8/Hx4bnnnmPOnDmMGDGCYcOGcejQIVavXl3lOhRjhg8fzkcffcSQIUO4//77SUlJYf78+TRv3rzKufDg4GA2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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from dataclasses import dataclass\n",
    "from typing import Optional, Tuple\n",
    "\n",
    "Tensor = torch.Tensor\n",
    "\n",
    "@dataclass\n",
    "class AttentionOutput:\n",
    "    hidden_states: Tensor\n",
    "    attn_scores: Tensor\n",
    "\n",
    "class MultiHeadAttention(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.hidden_size = config[\"d_model\"] # 隐藏层大小\n",
    "        self.num_heads = config[\"num_heads\"] # 多头注意力的头数\n",
    "        assert (\n",
    "            self.hidden_size % self.num_heads == 0\n",
    "        ), \"Hidden size must be divisible by num_heads but got {} and {}\".format(\n",
    "            self.hidden_size, self.num_heads\n",
    "        )\n",
    "        self.head_dim = self.hidden_size // self.num_heads # 每个头的维度\n",
    "\n",
    "        # layers\n",
    "        self.Wq = nn.Linear(self.hidden_size, self.hidden_size, bias=False) #第二个self.hidden_size可以*系数\n",
    "        self.Wk = nn.Linear(self.hidden_size, self.hidden_size, bias=False)\n",
    "        self.Wv = nn.Linear(self.hidden_size, self.hidden_size, bias=False)\n",
    "        self.Wo = nn.Linear(self.hidden_size, self.hidden_size, bias=False) # 输出层\n",
    "\n",
    "    def _split_heads(self, x: Tensor) -> Tensor:\n",
    "        bs, seq_len, _ = x.shape #假设输入的维度是[batch_size, seq_len, hidden_size],hidden_size是512\n",
    "        x = x.view(bs, seq_len, self.num_heads, self.head_dim) #num_heads是8，head_dim是64\n",
    "        return x.permute(0, 2, 1, 3) #变换维度，[batch_size, num_heads, seq_len, head_dim]\n",
    "\n",
    "    def _merge_heads(self, x: Tensor) -> Tensor:#将多头注意力的输出合并为一个张量\n",
    "        bs, _, seq_len, _ = x.shape #假设输入的维度是[batch_size, num_heads, seq_len, head_dim]\n",
    "        return x.permute(0, 2, 1, 3).reshape(bs, seq_len, self.hidden_size) # 变换维度，变为[batch_size, seq_len, hidden_size]\n",
    "\n",
    "    def forward(self, querys, keys, values, attn_mask=None) -> AttentionOutput:\n",
    "        # split heads\n",
    "        querys = self._split_heads(self.Wq(querys))\n",
    "        keys = self._split_heads(self.Wk(keys))\n",
    "        values = self._split_heads(self.Wv(values))\n",
    "\n",
    "        # calculate attention scores\n",
    "        qk_logits = torch.matmul(querys, keys.mT) # 计算注意力分数，matmul是矩阵乘法，mT是矩阵转置,qk_logits是[batch_size, num_heads, seq_len, seq_len]\n",
    "        # print(querys.shape[-2], keys.shape[-2])  #3 4\n",
    "        if attn_mask is not None:\n",
    "            attn_mask = attn_mask[:, :, : querys.shape[-2], : keys.shape[-2]]\n",
    "            qk_logits += attn_mask * -1e9 # 给需要mask的地方设置一个负无穷\n",
    "        attn_scores = F.softmax(qk_logits / (self.head_dim**0.5), dim=-1) # 计算注意力分数\n",
    "\n",
    "        # apply attention scores\n",
    "        embeds = torch.matmul(attn_scores, values) # softmax后的结果与value相乘，得到新的表示\n",
    "        embeds = self.Wo(self._merge_heads(embeds)) # 输出层\n",
    "\n",
    "        return AttentionOutput(hidden_states=embeds, attn_scores=attn_scores)\n",
    "\n",
    "mha = MultiHeadAttention({\"num_heads\": 2, \"d_model\": 2})\n",
    "query = torch.randn(2, 3, 2) # [batch_size, seq_len, hidden_size]\n",
    "query /= query.norm(dim=-1, keepdim=True) # 归一化\n",
    "key_value = torch.randn(2, 4, 2)\n",
    "print(f'key_value.shape {key_value.shape}')\n",
    "outputs = mha(query, key_value, key_value) #最终输出shape和query的shape一样\n",
    "print(outputs.hidden_states.shape)\n",
    "print(outputs.attn_scores.shape)\n",
    "# plt.subplots() 用于创建子图网格，其维度基于 outputs.attn_scores.shape[:2]。子图的行数和列数似乎由 outputs.attn_scores 的前两个维度确定。\n",
    "fig, axis = plt.subplots(*outputs.attn_scores.shape[:2])\n",
    "for i in range(query.shape[0]):\n",
    "    for j in range(outputs.attn_scores.shape[1]):\n",
    "        # axis[i, j].matshow(outputs.attn_scores[i, j].detach().numpy())：此行使用 Matplotlib 的 matshow 绘制每个 i 和 j 的注意力分数热图。detach().numpy() 将 PyTorch 张量转换为 NumPy 数组以进行可视化。\n",
    "        axis[i, j].matshow(outputs.attn_scores[i, j].detach().numpy())\n",
    "        for x in range(outputs.attn_scores.shape[2]):\n",
    "            for y in range(outputs.attn_scores.shape[3]):\n",
    "                # axis[i, j].text(y, x, f\"{outputs.attn_scores[i, j, x, y]:.2f}\", ha=\"center\", va=\"center\", color=\"w\")：此代码在热图上叠加文本，显示 (x, y) 位置处的注意力分数。格式化部分 f\"{outputs.attn_scores[i, j, x, y]:.2f}\" 确保以两位小数显示注意力分数。文本以白色居中显示在 (y, x) 坐标处。\n",
    "                axis[i, j].text(y, x, f\"{outputs.attn_scores[i, j, x, y]:.2f}\", ha=\"center\", va=\"center\", color=\"w\")\n",
    "fig.suptitle(\"multi head attention without mask\")\n",
    "plt.show()\n",
    "print('-'*50)\n",
    "# mask\n",
    "mask = torch.Tensor([[0, 0, 1, 1], [0, 0, 0, 1], [0, 0, 0, 0]]).reshape(1, 1, 3, 4) #手工构造mask\n",
    "outputs_masked = mha(query, key_value, key_value, mask)\n",
    "\n",
    "fig, axis = plt.subplots(*outputs_masked.attn_scores.shape[:2])\n",
    "for i in range(query.shape[0]):\n",
    "    for j in range(outputs_masked.attn_scores.shape[1]):\n",
    "        axis[i, j].matshow(outputs_masked.attn_scores[i, j].detach().numpy())\n",
    "        for x in range(outputs_masked.attn_scores.shape[2]):\n",
    "            for y in range(outputs_masked.attn_scores.shape[3]):\n",
    "                axis[i, j].text(y, x, f\"{outputs_masked.attn_scores[i, j, x, y]:.2f}\", ha=\"center\", va=\"center\", color=\"w\")\n",
    "fig.suptitle(\"multi head attention with mask\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "GkzzrRWVN4yE"
   },
   "source": [
    "#### Transformer-Block"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {
    "id": "b16xpL47N4yE",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:30.746901Z",
     "start_time": "2024-05-08T02:58:30.741904100Z"
    }
   },
   "outputs": [],
   "source": [
    "# 通过使用 @dataclass 装饰器，Python 会自动为该类生成一些方法，如 __init__()、__repr__() 和 __eq__() 等，这些方法可以使类的使用更加方便。\n",
    "@dataclass\n",
    "class TransformerBlockOutput:\n",
    "# hidden_states: Tensor：用于存储某个块产生的隐藏状态。\n",
    "# self_attn_scores: Tensor：包含了自注意力机制（self-attention）所计算得到的注意力分数。\n",
    "# cross_attn_scores: Optional[Tensor] = None：是一个可选字段，存储了交叉注意力（cross-attention）计算得到的注意力分数。这里的 Optional 表示这个字段可以是 Tensor 类型，也可以是 None。\n",
    "    hidden_states: Tensor\n",
    "    self_attn_scores: Tensor\n",
    "    cross_attn_scores: Optional[Tensor] = None\n",
    "\n",
    "class TransformerBlock(nn.Module):\n",
    "    def __init__(self, config, add_cross_attention=False):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.hidden_size = config[\"d_model\"]\n",
    "        self.num_heads = config[\"num_heads\"]\n",
    "        dropout_rate = config[\"dropout\"]\n",
    "        ffn_dim = config[\"dim_feedforward\"]\n",
    "        eps = config[\"layer_norm_eps\"] # 层归一化的epsilon值\n",
    "\n",
    "        # self-attention\n",
    "        self.self_atten = MultiHeadAttention(config) # 多头注意力\n",
    "        self.self_ln = nn.LayerNorm(self.hidden_size, eps=eps) #层归一化(层标准化)\n",
    "        self.self_dropout = nn.Dropout(dropout_rate)\n",
    "\n",
    "        # cross-attention，交叉注意力，decoder中使用,因此额外做一个判断\n",
    "        if add_cross_attention:\n",
    "            self.cross_atten = MultiHeadAttention(config)\n",
    "            self.cross_ln = nn.LayerNorm(self.hidden_size, eps=eps)\n",
    "            self.cross_dropout = nn.Dropout(dropout_rate)\n",
    "        else:\n",
    "            self.cross_atten = None\n",
    "\n",
    "        # FFN,前馈神经网络\n",
    "        self.ffn = nn.Sequential(\n",
    "            nn.Linear(self.hidden_size, ffn_dim),\n",
    "            nn.ReLU(),\n",
    "            nn.Linear(ffn_dim, self.hidden_size),\n",
    "        )\n",
    "        self.ffn_ln = nn.LayerNorm(self.hidden_size, eps=eps)\n",
    "        self.ffn_dropout = nn.Dropout(dropout_rate)\n",
    "\n",
    "    def forward(\n",
    "        self,\n",
    "        hidden_states,\n",
    "        attn_mask=None,\n",
    "        encoder_outputs=None,\n",
    "        cross_attn_mask=None,\n",
    "    ):\n",
    "        # self-attention,自注意力\n",
    "        self_atten_output = self.self_atten(\n",
    "            hidden_states, hidden_states, hidden_states, attn_mask\n",
    "        )\n",
    "        self_embeds = self.self_ln(\n",
    "            hidden_states + self.self_dropout(self_atten_output.hidden_states)\n",
    "        ) #多头注意力进行dropout，然后和原始输入进行残差连接，然后进行层归一化\n",
    "\n",
    "        # cross-attention，交叉注意力\n",
    "        if self.cross_atten is not None:\n",
    "            assert encoder_outputs is not None\n",
    "            cross_atten_output = self.cross_atten(\n",
    "                self_embeds, encoder_outputs, encoder_outputs, cross_attn_mask\n",
    "            ) #query是self_embeds，key和value都是encoder_outputs\n",
    "            cross_embeds = self.cross_ln(\n",
    "                self_embeds + self.cross_dropout(cross_atten_output.hidden_states)\n",
    "            ) # 交叉注意力进行dropout，然后和self_embeds进行残差连接，然后进行层归一化\n",
    "\n",
    "        # FFN\n",
    "        embeds = cross_embeds if self.cross_atten is not None else self_embeds # 如果有交叉注意力，则使用交叉注意力的输出作为FFN的输入；否则，使用self_embeds作为FFN的输入\n",
    "        ffn_output = self.ffn(embeds) # 前馈神经网络\n",
    "        embeds = self.ffn_ln(embeds + self.ffn_dropout(ffn_output)) # 前馈神经网络进行dropout，然后和原始输入进行残差连接，然后进行层归一化\n",
    "\n",
    "        return TransformerBlockOutput(\n",
    "            hidden_states=embeds,\n",
    "            self_attn_scores=self_atten_output.attn_scores,\n",
    "            cross_attn_scores=cross_atten_output.attn_scores\n",
    "            if self.cross_atten is not None\n",
    "            else None,\n",
    "        )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "tfJIGaohN4yE"
   },
   "source": [
    "#### Encoder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "id": "sTLabHm7N4yE",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:30.787877300Z",
     "start_time": "2024-05-08T02:58:30.753900900Z"
    }
   },
   "outputs": [],
   "source": [
    "from typing import List\n",
    "\n",
    "@dataclass\n",
    "class TransformerEncoderOutput:\n",
    "    last_hidden_states: Tensor\n",
    "    attn_scores: List[Tensor]\n",
    "\n",
    "# https://pytorch.org/docs/stable/generated/torch.nn.Module.html#torch.nn.Module\n",
    "class TransformerEncoder(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.num_layers = config[\"num_encoder_layers\"]\n",
    "\n",
    "        # layers\n",
    "        self.layers = nn.ModuleList(\n",
    "            [TransformerBlock(config) for _ in range(self.num_layers)]\n",
    "        )\n",
    "\n",
    "    def forward(\n",
    "        self, encoder_inputs_embeds, attn_mask=None\n",
    "    ) -> TransformerEncoderOutput:\n",
    "        attn_scores = [] # 存储每个层的注意力分数\n",
    "        embeds = encoder_inputs_embeds # 输入的嵌入向量作为第一层的输入(embedding+位置编码)\n",
    "        for layer in self.layers:\n",
    "            block_outputs = layer(embeds, attn_mask=attn_mask)\n",
    "            embeds = block_outputs.hidden_states #上一层的输出作为下一层的输入\n",
    "            # 在每个层的输出中，提取了隐藏状态 block_outputs.hidden_states，并将对应的注意力分数 block_outputs.self_attn_scores 添加到列表 attn_scores 中。\n",
    "            attn_scores.append(block_outputs.self_attn_scores) # 存储每个层的注意力分数,用于画图\n",
    "\n",
    "        return TransformerEncoderOutput(\n",
    "            last_hidden_states=embeds, attn_scores=attn_scores\n",
    "        )\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "BEMNj6eBN4yE"
   },
   "source": [
    "#### Decoder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "id": "wCHOur-QN4yE",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:30.811864100Z",
     "start_time": "2024-05-08T02:58:30.783880100Z"
    }
   },
   "outputs": [],
   "source": [
    "@dataclass\n",
    "class TransformerDecoderOutput:\n",
    "    last_hidden_states: Tensor\n",
    "    self_attn_scores: List[Tensor]\n",
    "    cross_attn_scores: List[Tensor]\n",
    "\n",
    "\n",
    "class TransformerDecoder(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.num_layers = config[\"num_decoder_layers\"]\n",
    "\n",
    "        # layers\n",
    "        self.layers = nn.ModuleList(\n",
    "            [\n",
    "                TransformerBlock(config, add_cross_attention=True)\n",
    "                for _ in range(self.num_layers)\n",
    "            ]\n",
    "        )\n",
    "\n",
    "    def forward(\n",
    "        self,\n",
    "        decoder_inputs_embeds,\n",
    "        encoder_outputs,\n",
    "        attn_mask=None,\n",
    "        cross_attn_mask=None,\n",
    "    ) -> TransformerDecoderOutput:\n",
    "        self_attn_scores = [] # 存储每个层的自注意力分数\n",
    "        cross_attn_scores = [] # 存储每个层的交叉注意力分数\n",
    "        embeds = decoder_inputs_embeds # 输入的嵌入向量作为第一层的输入(embedding+位置编码)\n",
    "        for layer in self.layers:\n",
    "            block_outputs = layer(\n",
    "                embeds,\n",
    "                attn_mask=attn_mask, # 自注意力的mask\n",
    "                encoder_outputs=encoder_outputs,\n",
    "                cross_attn_mask=cross_attn_mask, # 交叉注意力的mask\n",
    "            )\n",
    "            embeds = block_outputs.hidden_states # 上一层的输出作为下一层的输入\n",
    "            self_attn_scores.append(block_outputs.self_attn_scores) # 存储每个层的自注意力分数\n",
    "            cross_attn_scores.append(block_outputs.cross_attn_scores) # 存储每个层的交叉注意力分数\n",
    "\n",
    "        return TransformerDecoderOutput(\n",
    "            last_hidden_states=embeds,\n",
    "            self_attn_scores=self_attn_scores,\n",
    "            cross_attn_scores=cross_attn_scores,\n",
    "        )\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zPrvXAXtN4yF"
   },
   "source": [
    "#### mask\n",
    "\n",
    "- mask实际上大类上只有两种\n",
    "    1. `padding_mask`：mask掉`pad_idx`，不计算损失\n",
    "    2. `attention_mask`：mask掉`pad_idx`，不计算注意力分数\n",
    "- Decoder的`attention_mask`和Encoder有一定的区别：\n",
    "    - Encoder可以同时看见序列所有信息，故只mask掉`pad_idx`\n",
    "    - Decoder只能看到在自身之前的序列的信息，故要额外mask掉自身之后的序列"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "outputs": [
    {
     "data": {
      "text/plain": "tensor([[False,  True,  True,  True,  True],\n        [False, False,  True,  True,  True],\n        [False, False, False,  True,  True],\n        [False, False, False, False,  True],\n        [False, False, False, False, False]])"
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(torch.triu(torch.ones(5, 5)) == 0).transpose(-1,-2)"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:30.862835900Z",
     "start_time": "2024-05-08T02:58:30.799871700Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 426
    },
    "id": "QxpSYOsaN4yF",
    "outputId": "9974aaeb-c930-44b2-d7bb-c60b6d3dc443",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:31.438823200Z",
     "start_time": "2024-05-08T02:58:30.829854Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "<Figure size 480x480 with 2 Axes>",
      "image/png": "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GVOmTNE4HL8mRt0ndvPmTYSEhKBfv37YtWsXWrRogbNnz8LZ2dnQoRERGS1DPTsxNjYWsbGxGt/LzMysUtajRw/8/PPPdb7Ow4w6iaWkpMDT0xNr1qxRlbVt29aAERERSYREnn2oK6NuTtyxYwcCAwPx4osvwsXFBd26dcOqVasMHRYRkVGrrInpukmBUSex8+fPY8WKFejQoQP27NmDN954A5MnT8a6deuqPaasrKzKDHMiIjJNRt2cqFQqERgYiHnz5gEAunXrhhMnTiA9PR1RUVEaj0lOTsYHH3zQkGESERkXLsViHNzd3dG5c2e1sscffxyXLl2q9piEhAQUFxertry8vEcdJhGRkZHpaTN+Rl0TCwkJwenTp9XKzpw5gzZt2lR7jFwu19tjUYiIyLgZdU3srbfews8//4x58+bh3Llz2LhxI1auXFnnyXBERGbFAJOdDcWok1j37t2xbds2fP755/D19cWHH36ItLQ0jB071tChEREZLzNKYkbdnAgAQ4YMwZAhQwwdBhERGaF61cTy8vJw+fJl1eusrCzExcVh5cqVeguMiIjqqXIpFl03CahXEhszZgz2798PAMjPz8czzzyDrKwsvPfee5g9e7ZeAyQiorpp6KfYG1K9mhNPnDiBoKAgAMAXX3wBX19f/Pjjj/juu+8wceJEzJw5U69BEumDvlaIBrhKNJGxqFcSq6ioUA1j37dvH5577jkAQKdOndSeUkxERAbAyc418/HxQXp6Og4ePIi9e/di4MCBAICrV6+iWbNmeg2QiIjqiH1iNUtJScEnn3yCvn37YvTo0fDz8wPw4IG9lc2MREREj1q9mhP79u2LoqIilJSUqK3t9dprr6Fx48Z6C46IiOpOJh5sup5DCupVE0tKSsLly5erLE7p5eUFFxcXvQRGRET1ZEaTneuVxL766it4e3ujf//+2LhxI8rKyvQdFxER1Rf7xGqWk5ODI0eOwMfHB1OmTIGbmxveeOMNHDlyRN/xERERVavez07s1q0bPvroI1y9ehWffvopLl++jJCQEHTt2hVLlixBcXGxPuMkIiJtsTlRe0IIVFRUoLy8HEIIODs7Y+nSpfD09MTmzZv1ESMREdUFk1jtjh07htjYWLi7u+Ott95Ct27dcPLkSfzwww84e/Ys5s6di8mTJ+szViIiIjX1SmJdunTBk08+idzc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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\n",
    "def generate_square_subsequent_mask(sz: int) -> Tensor:\n",
    "    \"\"\"\n",
    "    Generate a square mask for the sequence. The masked positions are filled with True.\n",
    "        Unmasked positions are filled with False.\n",
    "    \"\"\"\n",
    "    # torch.ones(sz, sz): 创建一个全为 1 的 sz × sz 的矩阵。\n",
    "    # torch.triu(...): 使用 triu 函数取得矩阵的上三角部分，将主对角线以下部分置零。\n",
    "    mask = (torch.triu(torch.ones(sz, sz)) == 0).transpose(-1, -2).bool()\n",
    "    # mask = torch.triu(torch.ones(sz, sz))\n",
    "    return mask\n",
    "\n",
    "\n",
    "plt.matshow(generate_square_subsequent_mask(16))\n",
    "plt.colorbar()\n",
    "plt.xlabel(\"keys\")\n",
    "plt.ylabel(\"querys\")\n",
    "plt.title(\"1 means mask while 0 means unmask\")\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 926
    },
    "id": "cPeMjXO1N4yF",
    "outputId": "f13d6f0f-c016-4ee4-9dfe-fc4c05be5abb",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:32.779465600Z",
     "start_time": "2024-05-08T02:58:31.447816600Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['[BOS]', '[UNK]', '[UNK]', 'brown', '[UNK]', 'jumps', 'over', 'the', '[UNK]', 'dog', '.', '[EOS]']\n"
     ]
    },
    {
     "data": {
      "text/plain": "<Figure size 1000x500 with 2 Axes>",
      "image/png": "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rFn64x//qDFjxuiFF17QNddcIw8PD02ePNlpPzt27Kh9+/bpk08+0b/+9S/9/e9/15tvvqnnnntOM2fOdNr+b37zG7333ntKTk7WI488YmpfAKAu8/Hx0d133601a9boX//6l+69917T23j88ce1ePFiTZ48Wb/61a9ks9lksViUkJBQYf+50mt92XOWLl2q4ODgcut5ef3yv5au6H0VoY/BVQhSqJeuu+46PfbYY3rsscf0448/qnv37vrTn/6kV155RZJktVp11113uaSW06dPKy0tTTNnztRzzz3nGC97V+9Cl/sE97rw6e4ffvih7rjjDr399ttO42fOnCl3Ua2fn5/i4+MVHx+vwsJC3X///frTn/6kqVOnOp3yMnfuXHl5eemxxx5T06ZNL3kRLwC4yw033CBJ2rdvn9q0aeMYLyws1KFDhxz95sL17rzzTqdt7Nu3z/F4GYvFouTkZA0aNEhDhgzR2rVrFRMTY6q2Dz/8UCNHjtS8efMcY/n5+RXeTbAyym501Lx588v20cr2vqCgIPn6+lbYE/ft21elGq8GfQxmcGof6pWSkpJypyo0b95cISEhKigoUHh4uNq2bauXXnqpwlvHVnSL16tV9g7YxUeGXn311XLr+vn5SVKFDc7Pz6/Kjc9VPD09y+3n3/72t3K39b341reNGjXSzTffLMMwVFRU5PSYxWLRwoUL9dvf/lYjR47Uxx9/XDPFA0AV3XXXXWrUqJFee+01p9fAt99+W7m5ubrnnnskST169FDz5s21YMECFRQUONZbu3at9uzZ41jvQo0aNdKqVasUERGhgQMHatu2baZqq+h1+fXXX1dJSYmp7ZSJjY2V1WrVrFmzyr1eS7/00cr2Pk9PT8XGxuqjjz7S999/7xjfs2ePUlJSqlTj1aCPwQyOSKFeycvLU2hoqH7729/q1ltvlb+/v9atW6ft27dr3rx58vDw0KJFixQXF6dOnTpp9OjRatmypY4ePar169fLarXqH//4R7XWZLVaHbdgLSoqUsuWLZWamqpDhw6VWzc8PFySNG3aNCUkJMjb21sDBw6Un5+fwsPDtW7dOr388ssKCQlR69at1bNnz2qt9Wrde++9ev755zV69Gjdfvvt2r17t5KTk53eoZWk/v37Kzg4WL1791aLFi20Z88evfHGG7rnnnvUtGnTctv18PDQsmXLNHjwYA0dOlSffvppuXdzAcBdgoKCNHXqVM2cOVMDBgzQr3/9a+3bt09vvvmmIiIiNHz4cEmSt7e3Zs+erdGjRys6OloPPPCA4/bnrVq10hNPPFHh9n19ffXJJ5/ozjvvVFxcnDZs2KBbbrmlUrXde++9Wrp0qWw2m26++WZt2bJF69atU7Nmzaq0r1arVfPnz9dDDz2k7t27KyEhQUFBQfr+++/1z3/+U71799Ybb7xhqvfNnDlT//rXvxQZGanHHntMxcXFjs9o2rVrV5XqrCr6GExxz80CgZpRUFBgPPnkk8att95qNG3a1PDz8zNuvfVW480333Rab+fOncb9999vNGvWzPDx8TFuuOEGY+jQoUZaWppjneq8/Xl2drZx3333GQEBAYbNZjOGDBli5OTklLvtqmEYxgsvvGC0bNnS8PDwcJp/7969RlRUlOHr62tIctwK/cLbq16oovqv5FL7Fx0dXeFtXm+44QbjnnvucXyfn59vTJkyxbjuuusMX19fo3fv3saWLVuM6OhoIzo62rHeW2+9ZURFRTl+/m3btjWefPJJIzc317FORft1/vx5Izo62vD39ze2bt1a6f0CgOpW0WvsG2+8Ydx0002Gt7e30aJFC2P8+PHG6dOnyz13xYoVRrdu3QwfHx/jmmuuMR588EEjOzvbaZ2KXo9PnDhh3HzzzUZwcLCxf//+StV5+vRpY/To0ca1115r+Pv7G7GxscbevXuNG264wekjNcr2Z/v27U7PX79+vSHJWL9+fbnx2NhYw2azGY0bNzbatm1rjBo1ytixY4djHTO9b8OGDUZ4eLjRqFEjo02bNsaCBQscfcAM+hhcyWIYJq9EBwAAAIAGjmukAAAAAMAkrpECrkJubq5++umny65T0e1hXa2u1AkAqJyzZ89WeNOkCwUFBV3ylt91DX0MtRGn9gFXYdSoUXrvvfcuu05t+CdWV+oEAFTOjBkzyn1e0cUOHTqkVq1auaagGkYfQ21EkAKuwrfffqucnJzLruOqz6u6nLpSJwCgcg4ePKiDBw9edp0+ffo4fZ5RXUYfQ21EkAIAAAAAk7jZBAAAAACYRJACAAAAAJMIUnCp9PR0JSYmursMl8jMzNT8+fPdXYbbHD58WKmpqZKkHj16uLka1/j66681atQod5cBwCR6U8NBb0J1IkhVID09XWFhYVq4cKFiYmIUGRmpqKgoDRs2TCUlJZKk3bt3q2/fvoqOjta9996rrKwsSdKuXbsUFRWl6Oho3X777Tp69Ki+/fZbde3a9bIv0hfOefE/7LLvR40apbi4uHLjFzaALVu2qHfv3jpz5oxGjBih0NDQWjXn5ZSWllbpeZXhjn3t2rWrxo8fX9274lJX8zu5sFnVhNr291sfVfRaGBMTo5iYGOXm5qq0tFTPPvusIiMj1adPH7322muO5yYmJqp3797q06ePXnjhBUnSU089pYCAgCveshnluaMvXTwvvan60Zuqht7UsNWm3kSQuoT4+HiNGzdOkrR27VplZGTI399fW7ZsUVFRkYYPH66FCxdqw4YNmjp1qoYPHy5JeuGFFzR//nxt2LBBaWlpatasmW6++Wa9+uqrpua8lOzsbO3atavCx3bv3q1JkyZp1apVCggI0JIlS674mQrumHPXrl0aOHCgIiIitHv3bnXv3l2TJk3SQw89pOzsbN11112KiorSxIkTJUkPPvigcnJylJaWprZt20qSZs6cqfXr12vGjBl66KGHdPfddys6OvqynzHh6n0te0G88IW07OsZM2Zo+PDhiouLU1xcnObPn6+YmBjFx8dLkt59910NHjxYd999tyIjI3X06FGdOnVKMTExuuOOOzRo0KDL7ocklZSUaPjw4YqOjtY999yjuXPnasWKFZKk7777Tg888IAkadasWYqOjlZUVJR2794tSU6/k6qaP3++VqxYoZiYGJ07d04jR45U165dlZycLOnnO07FxsYqJiZGTzzxRJXmcMff78WKi4s1dOhQ3XXXXXrllVckScuXL1fPnj3Vq1cvpaSkSJJSU1PVrVs3DRkyRFFRUTp8+LCpedzl4tfC9PR0paeny2az6e2339apU6e0ceNGpaenKyUlRevWrdM333yjI0eOaPPmzdq0aZPj3/KcOXPUtWtXN+5N3eaOvnTxvJdCb6I30Zt+QW+qebWlNxGkTMjLy5PVatXWrVvVtWtXxwtn7969VVpaqqysLPn6+mrdunU6d+6cfH19q/22o4mJiZozZ0658UOHDmnMmDFauXKlWrRoUavnPH/+vD7++GMtWbJE06ZN0+nTp/X4448rOTlZf/7zn5WYmKiMjAz99NNPysjIUJ8+fbRx40Zt3LhRHTp00NGjR/Xll1+qV69ekqT27dvr008/Va9evfTZZ5/Vqn29nI4dO2rt2rUKDAxUYWGh0tPTVVhY6LidbZMmTfTpp59q2rRpmj17tnbu3KnbbrtN69ev1+rVq6+4/dWrVys0NFQbNmxQQkKCzp8/r5UrV0qSVqxYofj4eH399dfat2+fNmzYoOXLl+vZZ5+VJKffSVWNHz9e8fHxSk9P17Fjx/T6668rIyPD8c7QM888ozfffFPp6enKz8/Xjh07qjzX5dT07/Sjjz5Su3bttG7dOkVERKikpERJSUnasGGDUlNTNW3aNEnSc889p7S0NC1btsxxpKCuW758uZ588klJkpeXl/7whz/ogw8+UOPGjbV//37t2bNHkhQYGOjOMuu12tCXJHoTvYneZBa9qea4sjcRpCohLi5O3bp1U3Z2tjp27KicnByFhIQ4rRMaGqqcnBzNnTtXe/bs0a233qr4+HidO3euWmsJDw/XiRMndOTIEafxtLQ09ezZs0Y+eK+65+zWrZssFos6duyo//3vfwoMDFS7du0kSQcOHFBERIQkKSIiQvv371dkZKQyMjK0b98+jR07VmlpaSouLpavr69je5IUFham06dP16p9vdiFnzbQpUsXSVJISIjj65YtWzr2ITw8XNIvP4fo6Gj5+fnpwQcf1Msvv3zFuS7+WR44cEC5ubmy2+1KSUlRXFycvv32W33xxReKiYnRsGHDHIe1L/ydVIc2bdrIarXKarU6TkPau3evxo4dq5iYGG3btk3Z2dnVNt+Favp3euDAAaff1fHjx3X99dercePGslqt8vb2VnFxsUpKSnTNNdfIx8dHt9xyy1XN6S5xcXGKiYlxnJJy8Wth2etg27Zt9cwzz+ixxx7TjTfeqDVr1rir5HqrNvUlid5Eb6I3mUVvqj7u7E0EqUpYu3atdu7cqSFDhmjevHm67rrryn0oXHZ2tkJCQtSiRQstWLBABw4cUPv27bV06VLT81ksFsfX+fn5jhflMlOmTNG8efOcxsaMGaOjR4/qnXfeMT2fq+fMzMyUYRjat2+frrvuOnl4/PJn2K5dO23btk2StH37drVv316dOnVSZmamGjVqpKioKL322mvq3r17hbVX5mPRXP3z9fT0VF5envLy8pw+PPHCOirah507d0qSduzYoXbt2qmoqEjTp09XcnKyUlNT9f3331923op+loMHD9bs2bPVpk0b+fj46KabblJ0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"
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------\n",
      "['[BOS]', '[UNK]', 'does', 'the', '[UNK]', 'say', '?', '[EOS]', '[PAD]', '[PAD]', '[PAD]', '[PAD]']\n"
     ]
    },
    {
     "data": {
      "text/plain": "<Figure size 1000x500 with 2 Axes>",
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA1AAAAG1CAYAAAD3DRUpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguNCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8fJSN1AAAACXBIWXMAAA9hAAAPYQGoP6dpAABHu0lEQVR4nO3de1xUdf7H8TeIAoIzWOGFIFMzzTItvP1UZMTk0mUp826rqJub2oalbvro4q0kI9MuW8q6aRc3M6XazUi84SUtbVPRTV3LSyBamgreQC7n98c+mGUC8YA4M8Dr+XjMY5kzZ76fz2GFdx/OzBkPwzAMAQAAAACuyNPVDQAAAABAdcEABQAAAAAmMUABAAAAgEkMUAAAAABgEgMUAAAAAJjEAAUAAAAAJjFAAQAAAIBJDFAAAAAAYBIDFAAAAACYxAAFlGHx4sXy8PDQ4cOHHbYnJiaqRYsWqlOnjjp06OCS3mqDadOmycPDQydPnnR1KwBQyuUyoqrExcXJ39//mqxdluLj+fbbb51W80qKc6C6IsdqNgYowKTU1FT9+c9/Vvfu3bVo0SLNmjWrStffsmWLpk2bpjNnzpR6bNasWfr000+rtB4AAAAqjgEKMGndunXy9PTU3/72Nw0bNkz33ntvla6/ZcsWTZ8+nQEKAADAjTFAASb98ssv8vX1Vb169VzdCgAAAFyEAQo1ytmzZzV+/HjdfPPN8vb2VqNGjdSnTx9999139n2++eYbRUdHy2q1qn79+goPD9dXX31V7roeHh5atGiRzp8/Lw8PD3l4eGjx4sWmekpPT1dcXJxatGghHx8fNWnSRCNHjtSvv/5q32fatGmaNGmSJKl58+b2GocPH5aHh4fOnz+vd9991749Li7O/jwPDw/98MMPiouLU0BAgKxWq0aMGKELFy5U6HtX/Jr7n376Sffff7/8/f1144036i9/+Yskaffu3YqIiJCfn5+aNWumv//97w7PP3XqlCZOnKh27drJ399fFotFMTEx2rVrV6lab7zxhm6//XbVr19fDRs2VMeOHUut91tHjhzRLbfcojvuuEM///xzhY4NAK61t956S7fffru8vb0VFBSkcePGlfmKgo8//lihoaHy9fXVDTfcoEceeURHjx694vo7d+5UYGCgbDabzp07Z6qnI0eOaOzYsWrdurV8fX11/fXXq3///pd971ZeXp6eeuopBQYGys/PTw899JBOnDhRar+UlBSFhYXJz89PDRo00H333ad///vfDvuYyb5imzdvVqdOneTj46OWLVtqwYIFpo7vt8gxOIuXqxsAqtJjjz2m5cuX6/HHH1fbtm3166+/avPmzdq7d6/uvvturVu3TjExMQoNDdXUqVPl6empRYsWKSIiQps2bVLnzp3LXPf9999XUlKStm3bpoULF0qSunXrZqqn1atX6+DBgxoxYoSaNGmif//730pKStK///1vff311/Lw8FDfvn31n//8Rx9++KHmzp2rG264QZIUGBio999/X3/4wx/UuXNnjR49WpLUsmVLhxoDBgxQ8+bNlZCQoO+++04LFy5Uo0aNNHv27Ap9/woLCxUTE6OePXvq5Zdf1pIlS/T444/Lz89PzzzzjIYOHaq+fftq/vz5GjZsmP7v//5PzZs3lyQdPHhQn376qfr376/mzZvr559/1oIFCxQeHq7vv/9eQUFBkqS//vWveuKJJ9SvXz/Fx8crNzdX6enp+uabbzRkyJAy+/rxxx8VERGh6667TqtXr7Z/fwDAHUybNk3Tp0/XPffcozFjxmj//v16++23tX37dn311VeqW7eupP9erGHEiBHq1KmTEhIS9PPPP+u1117TV199pR07diggIKDM9bdv366oqCh17NhRn332mXx9fU31tX37dm3ZskWDBg1ScHCwDh8+rLfffls2m03ff/+96tev77D/n/70JzVs2FBTp07V4cOHNW/ePD3++OP66KOP7Pu8//77Gj58uKKiojR79mxduHBBb7/9tnr06KEdO3bo5ptvlmQu+6T/DjWRkZEKDAzUtGnTVFBQoKlTp6px48YV/H/hv8gxOIUB1CBWq9UYN25cmY8VFRUZrVq1MqKiooyioiL79gsXLhjNmzc3+vTpY9+2aNEiQ5Jx6NAh+7bhw4cbfn5+Fe7pwoULpbZ9+OGHhiRj48aN9m2JiYmlahbz8/Mzhg8fXmr71KlTDUnGyJEjHbY/9NBDxvXXX1+hPocPH25IMmbNmmXfdvr0acPX19fw8PAwli5dat++b98+Q5IxdepU+7bc3FyjsLDQYc1Dhw4Z3t7exowZM+zbYmNjjdtvv73cXoqP68SJE8bevXuNoKAgo1OnTsapU6cqdEwAcC2UzIhffvnFqFevnhEZGenwO/DNN980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9cEHH+jBBx/UgAED9MUXX5T66y0AuEpgYKCmTJmi6dOnKzo6Wr/73e+0f/9+vfXWW+rUqZMeeeQRSVLdunU1e/ZsjRgxQuHh4Ro8eLD9MuY333yznnzyyTLX9/X11eeff66IiAjFxMRow4YNuuOOO0z1dv/99+v999+X1WpV27ZttXXrVq1Zs0bXX399pY7VYrHo7bff1u9//3vdfffdGjRokAIDA/XTTz9p5cqV6t69u958880KZd/06dP15ZdfKiwsTGPHjlVBQYH9M5bS09Mr1WdlkWMwzTUX/wOqXl5enjFp0iSjffv2RoMGDQw/Pz+jffv2xltvveWw344dO4y+ffsa119/veHt7W00a9bMGDBggLF27Vr7PlV5GfPMzEzjoYceMgICAgyr1Wr079/fyMrKKnX5VMMwjJkzZxo33nij4enp6VB/3759Rs+ePQ1fX19Dkv2S5iUvk1pSWf1fyeWOLzw8vMzLtTZr1sy477777Pdzc3ONCRMmGE2bNjV8fX2N7t27G1u3bjXCw8ON8PBw+34LFiwwevbsaf/+t2zZ0pg0aZKRnZ1t36es47pw4YIRHh5u+Pv7G19//bXp4wKAqlbW79g333zTaNOmjVG3bl2jcePGxpgxY4zTp0+Xeu5HH31k3HXXXYa3t7dx3XXXGUOHDjUyMzMd9inr9/HJkyeNtm3bGk2aNDEOHDhgqs/Tp08bI0aMMG644QbD39/fiIqKMvbt22c0a9bM4aMxio9n+/btDs9fv369IclYv359qe1RUVGG1Wo1fHx8jJYtWxpxcXHGt99+a9+nItm3YcMGIzQ01KhXr57RokULY/78+fYcqAhyDM7iYRgVfJc5AAAAANRSvAcKAAAAAEziPVBAJWVnZ+vixYvl7lPWZV6drbr0CQAw59y5c2VeDKmkwMDAy166u7ohx+BueAkfUElxcXF69913y93HHX68qkufAABzpk2bVurzhn7r0KFDuvnmm53T0DVGjsHdMEABlfT9998rKyur3H2c9XlT5akufQIAzDl48KAOHjxY7j49evRw+Dyi6owcg7thgAIAAAAAk7iIBAAAAACYxAAFAAAAACYxQMFuz549iouLc2rNw4cPKzU1VZLUsWNHp9aurb755ht1795dd999tz744ANXtwMAl+WKXJLIJlcgm1CdMECVkJaWppCQECUlJclmsyksLEw9e/bUkCFDVFhYKEnavXu3evfurfDwcN1///3KyMiQJKWnp6tnz54KDw9Xt27ddPToUX3//ffq0KGDJk6caKrmb39JF9+Pi4tTTExMqe1paWn2tbdu3aru3bvrzJkzGjZsmIKDg6vuG3MNlQypa6G2f3/L0rRpU61bt05btmzRa6+9dtXrlfVzY7PZZLPZlJ2draKiIj377LMKCwtTjx499Prrr9ufO3HiRHXv3l09evTQzJkzJUl//vOfFRAQUO4lesuq2blzZ/saktStWzfNmDHDfn/x4sVq1aqVIiIiFBYWpgULFtgfi4yMNPUfSa6oW1tq4vLIJucjm5yPbCKb3LHmZRmwW79+vTFhwgTDMAwjPDzcOHv2rGEYhvHoo48amzZtMi5dumTceeedxg8//GAYhmFs3rzZ6Nmzp2EYhtGvXz9jz549hmEYxoULF4yLFy+WWvNKNUNDQx0eK74/fPhw44477jB27drlsL34uenp6UanTp2M48ePl3ruleTn5xv9+/c3evfubYwcOdIYPny48eGHHxqdO3c2unTpYnz55ZeGYRjG9u3bDZvNZvTo0cNITEw0DMMw3n77baNTp05Gr169jOTkZFP1fmvAgAFGcHCwER4ebrRp08YYNmyY0b59e+ODDz4wDMMwfvzxRyMyMtIIDw83xo8fX+H1Xf39LWnr1q1G586dDZvNZkydOtV48sknjZ49exqdOnUyduzYYfzyyy/Gvffea98/IiLCyM7OrnAdszZs2GAMHTr0qte53M9NsaSkJGPMmDGGYfz339u9995rrF692tizZ4/Rr18/+36nTp2yf13WOleqmZ+fb9x5551GRkaG8dNPPxn9+/c3evXqZX/OokWLjDfeeMMwDMM4f/68ERkZaXz++ef2x838f+qKurWlJi6vtmWTq3PJMGpPNrlbLhkG2UQ2uVfNy+EMlAlnz56VxWLR119/rQ4dOqhly5aSpO7du6uoqEgZGRny9fXVmjVrdP78efn6+lb5pUMnTpyol19+udT2Q4cOaeTIkVq2bJkaN25c4XU//fRT3XLLLVqzZo06deqkwsJCJSQkaMOGDUpNTdUzzzwjSZo8ebKSk5O1adMmbdiwQT///LOWLVumNWvWaN26dYqNja3UcY0ZM0YDBw5UWlqajh8/rjfeeEMbN260/yVo8uTJeuutt5SW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"
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "--------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "#通过下面代码查看mask的效果\n",
    "inputs_words = [\"The quick brown fox jumps over the lazy dog .\", \"What does the fox say ?\"]\n",
    "\n",
    "inputs_ids, input_mask = tokenizer.encode([w.split() for w in inputs_words], return_mask=True)\n",
    "for i in range(len(inputs_words)):\n",
    "    decode_text = tokenizer.decode(inputs_ids[i: i+1].tolist(), remove_bos=False, remove_eos=False, remove_pad=False, split=True)[0]\n",
    "    print(decode_text)\n",
    "    self_attn_mask  = input_mask[i].reshape(1, -1).repeat_interleave(inputs_ids.shape[-1], dim=0)\n",
    "    look_ahead_mask = generate_square_subsequent_mask(inputs_ids.shape[-1])\n",
    "\n",
    "    fig, axs = plt.subplots(1, 2, figsize=(10, 5))\n",
    "    axs[0].matshow(self_attn_mask)\n",
    "    axs[0].set_title(\"self_attn_mask\")\n",
    "    axs[0].set_yticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[0].set_ylabel(\"querys\")\n",
    "    axs[0].set_xticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[0].set_xlabel(\"keys\")\n",
    "    axs[1].matshow(look_ahead_mask)\n",
    "    axs[1].set_title(\"look_ahead_mask\")\n",
    "    axs[1].set_yticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[1].set_ylabel(\"querys\")\n",
    "    axs[1].set_xticks(range(len(decode_text)), decode_text, fontsize=6)\n",
    "    axs[1].set_xlabel(\"keys\")\n",
    "    plt.show()\n",
    "    print('-'*50)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "TelCDOoEN4yF"
   },
   "source": [
    "#### Transformer Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "id": "oFGNt8FPN4yF",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:32.838429600Z",
     "start_time": "2024-05-08T02:58:32.782460800Z"
    }
   },
   "outputs": [],
   "source": [
    "@dataclass\n",
    "class TransformerOutput:\n",
    "    logits: Tensor\n",
    "    encoder_last_hidden_states: Tensor\n",
    "    encoder_attn_scores: List[Tensor]\n",
    "    decoder_last_hidden_states: Tensor\n",
    "    decoder_self_attn_scores: List[Tensor]\n",
    "    decoder_cross_attn_scores: List[Tensor]\n",
    "    preds: Optional[Tensor] = None\n",
    "\n",
    "class TransformerModel(nn.Module):\n",
    "    def __init__(self, config):\n",
    "        super().__init__()\n",
    "        # hyper params\n",
    "        self.hidden_size = config[\"d_model\"]\n",
    "        self.num_encoder_layers = config[\"num_encoder_layers\"]\n",
    "        self.num_decoder_layers = config[\"num_decoder_layers\"]\n",
    "        self.pad_idx = config[\"pad_idx\"]\n",
    "        self.bos_idx = config[\"bos_idx\"]\n",
    "        self.eos_idx = config[\"eos_idx\"]\n",
    "        self.vocab_size = config[\"vocab_size\"]\n",
    "        self.dropout_rate = config[\"dropout\"]\n",
    "        self.max_length = config[\"max_length\"]\n",
    "        self.share = config[\"share_embedding\"]\n",
    "\n",
    "        # layers\n",
    "        self.src_embedding = TransformerEmbedding(config) # 输入的嵌入层\n",
    "        if self.share:#如果共享词嵌入，则使用src_embedding作为trg_embedding\n",
    "            self.trg_embedding = self.src_embedding #源和目标的嵌入层相同，共享参数，节省内存\n",
    "            self.linear = lambda x: torch.matmul(\n",
    "                x, self.trg_embedding.get_word_embedding_weights().T\n",
    "            ) # 输出层，共享参数，直接拿原有embedding矩阵的转置，节省内存\n",
    "        else:\n",
    "            self.trg_embedding = TransformerEmbedding(config) #decoder模块的嵌入层\n",
    "            self.linear = nn.Linear(self.hidden_size, self.vocab_size) # 输出层\n",
    "\n",
    "        self.encoder = TransformerEncoder(config)\n",
    "        self.decoder = TransformerDecoder(config)\n",
    "\n",
    "        # init weights\n",
    "        self._init_weights()\n",
    "\n",
    "    def _init_weights(self):\n",
    "        \"\"\"使用 xavier 均匀分布来初始化权重\"\"\"\n",
    "        for p in self.parameters():\n",
    "            if p.dim() > 1:\n",
    "                nn.init.xavier_uniform_(p)\n",
    "\n",
    "    def generate_square_subsequent_mask(self, sz: int) -> Tensor:\n",
    "        \"\"\"\n",
    "        Generate a square mask for the sequence. The masked positions are filled with True.\n",
    "            Unmasked positions are filled with False.为了生成斜三角的mask\n",
    "        \"\"\"\n",
    "        mask = (torch.triu(torch.ones(sz, sz)) == 0).transpose(-1, -2).bool()\n",
    "\n",
    "        return mask\n",
    "\n",
    "    def forward(\n",
    "        self, encoder_inputs, decoder_inputs, encoder_inputs_mask=None\n",
    "    ) -> TransformerOutput:\n",
    "        # encoder_inputs: [batch_size, src_len]\n",
    "        # decoder_inputs: [batch_size, trg_len]\n",
    "        # encoder_inputs_mask: [batch_size, src_len]\n",
    "        if encoder_inputs_mask is None:\n",
    "            encoder_inputs_mask = encoder_inputs.eq(self.pad_idx) # [batch_size, src_len]\n",
    "        encoder_inputs_mask = encoder_inputs_mask.unsqueeze(1).unsqueeze(\n",
    "            2\n",
    "        )  # [batch_size, 1, 1, src_len],用于encoder的自注意力\n",
    "        look_ahead_mask = self.generate_square_subsequent_mask(decoder_inputs.shape[1])\n",
    "        look_ahead_mask = (\n",
    "            look_ahead_mask.unsqueeze(0).unsqueeze(0).to(decoder_inputs.device)\n",
    "        )  # [1, 1, trg_len, trg_len],用于decoder的自注意力\n",
    "        #增加decoder_inputs_mask和look_ahead_mask进行组合\n",
    "        decoder_inputs_mask = decoder_inputs.eq(self.pad_idx) # [batch_size, trg_len]\n",
    "        print(decoder_inputs_mask.shape)\n",
    "        decoder_inputs_mask = decoder_inputs_mask.unsqueeze(1).unsqueeze(2)  # [batch_size, 1, 1, trg_len]\n",
    "        print(decoder_inputs_mask.shape)\n",
    "        decoder_inputs_mask = decoder_inputs_mask + look_ahead_mask # [batch_size, 1, 1, trg_len]与[1, 1, trg_len, trg_len]相加，得到decoder的自注意力mask\n",
    "\n",
    "        # encoding\n",
    "        encoder_inputs_embeds = self.src_embedding(encoder_inputs)\n",
    "        encoder_outputs = self.encoder(encoder_inputs_embeds, encoder_inputs_mask) #encoder_inputs_mask用于encoder的自注意力,广播去做计算\n",
    "\n",
    "        # decoding\n",
    "        decoder_inputs_embeds = self.trg_embedding(decoder_inputs)\n",
    "        decoder_outputs = self.decoder(\n",
    "            decoder_inputs_embeds=decoder_inputs_embeds,\n",
    "            encoder_outputs=encoder_outputs.last_hidden_states,\n",
    "            attn_mask=decoder_inputs_mask, #用于decoder的自注意力,广播去做计算\n",
    "            cross_attn_mask=encoder_inputs_mask,#用于decoder的交叉注意力,广播去做计算\n",
    "        )\n",
    "\n",
    "        logits = self.linear(decoder_outputs.last_hidden_states)\n",
    "\n",
    "        return TransformerOutput(\n",
    "            logits=logits,\n",
    "            encoder_last_hidden_states=encoder_outputs.last_hidden_states,\n",
    "            encoder_attn_scores=encoder_outputs.attn_scores,\n",
    "            decoder_last_hidden_states=decoder_outputs.last_hidden_states,\n",
    "            decoder_self_attn_scores=decoder_outputs.self_attn_scores,\n",
    "            decoder_cross_attn_scores=decoder_outputs.cross_attn_scores,\n",
    "        )\n",
    "\n",
    "    @torch.no_grad()\n",
    "    def infer(self, encoder_inputs, encoder_inputs_mask=None) -> Tensor:\n",
    "        # assert len(encoder_inputs.shape) == 2 and encoder_inputs.shape[0] == 1\n",
    "        if encoder_inputs_mask is None:#应对多个样本同时进行推理\n",
    "            encoder_inputs_mask = encoder_inputs.eq(self.pad_idx)\n",
    "        encoder_inputs_mask = encoder_inputs_mask.unsqueeze(1).unsqueeze(2)  # [batch_size, 1, 1, src_len],[1,src_len]相加时，会自动广播到[batch_size,1,src_len,src_len]\n",
    "        look_ahead_mask = self.generate_square_subsequent_mask(self.max_length)\n",
    "        look_ahead_mask = (\n",
    "            look_ahead_mask.unsqueeze(0).unsqueeze(0).to(encoder_inputs.device)\n",
    "        )  # [1, 1, trg_len, trg_len]\n",
    "\n",
    "        # encoding\n",
    "        encoder_inputs_embeds = self.src_embedding(encoder_inputs)\n",
    "        encoder_outputs = self.encoder(encoder_inputs_embeds) #因为只支持单样本预测，没有paddings，所以不需要mask\n",
    "\n",
    "        # decoding\n",
    "        decoder_inputs = torch.Tensor([self.bos_idx] * encoder_inputs.shape[0]).reshape(-1, 1).long().to(device=encoder_inputs.device)\n",
    "        for cur_len in tqdm(range(1, self.max_length + 1)):\n",
    "            decoder_inputs_embeds = self.trg_embedding(decoder_inputs)\n",
    "            decoder_outputs = self.decoder(\n",
    "                decoder_inputs_embeds=decoder_inputs_embeds,\n",
    "                encoder_outputs=encoder_outputs.last_hidden_states,\n",
    "                attn_mask=look_ahead_mask[:, :, :cur_len, :cur_len],#decoder的自注意力mask\n",
    "            )\n",
    "\n",
    "            logits = self.linear(decoder_outputs.last_hidden_states)\n",
    "            next_token = logits.argmax(dim=-1)[:, -1:] #通过最大下标确定类别，[:, -1:]表示取最后一个结果\n",
    "            decoder_inputs = torch.cat([decoder_inputs, next_token], dim=-1) #预测输出拼接到输入中\n",
    "\n",
    "\n",
    "\n",
    "            if all((decoder_inputs == self.eos_idx).sum(dim=-1) > 0):#如果输出所有序列中出现了eos_idx，则停止预测\n",
    "                break\n",
    "            #如果某个样本到达结束标记，把其从decoder_inputs中移除,减少预测计算量\n",
    "            # decoder_inputs=decoder_inputs[(decoder_inputs == self.eos_idx).sum(dim=-1)==0]\n",
    "\n",
    "        return TransformerOutput(\n",
    "            preds=decoder_inputs[:, 1:],\n",
    "            logits=logits,\n",
    "            encoder_last_hidden_states=encoder_outputs.last_hidden_states,\n",
    "            encoder_attn_scores=encoder_outputs.attn_scores,\n",
    "            decoder_last_hidden_states=decoder_outputs.last_hidden_states,\n",
    "            decoder_self_attn_scores=decoder_outputs.self_attn_scores,\n",
    "            decoder_cross_attn_scores=decoder_outputs.cross_attn_scores,\n",
    "        )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "outputs": [
    {
     "data": {
      "text/plain": "tensor([[ 0.5200,  0.5567,  0.0744],\n        [ 0.7113, -0.5687,  1.2580]])"
     },
     "execution_count": 37,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#随机一个二维tensor\n",
    "x = torch.randn(2, 3)\n",
    "x"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:32.853422700Z",
     "start_time": "2024-05-08T02:58:32.830434800Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "outputs": [
    {
     "data": {
      "text/plain": "tensor([], size=(0, 3))"
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x[[False,False]]"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:32.863416700Z",
     "start_time": "2024-05-08T02:58:32.844426100Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "outputs": [
    {
     "data": {
      "text/plain": "False"
     },
     "execution_count": 39,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "all([1,0,0])"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:32.931377200Z",
     "start_time": "2024-05-08T02:58:32.864417100Z"
    }
   }
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "kXHQ3sGTN4yG"
   },
   "source": [
    "## 训练"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "OxNA8DIzN4yG"
   },
   "source": [
    "### 损失函数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "id": "1xzahHANN4yG",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:32.932376400Z",
     "start_time": "2024-05-08T02:58:32.879407Z"
    }
   },
   "outputs": [],
   "source": [
    "class CrossEntropyWithPadding:\n",
    "    def __init__(self, config):\n",
    "        self.label_smoothing = config[\"label_smoothing\"]\n",
    "\n",
    "    def __call__(self, logits, labels, padding_mask=None):\n",
    "        # logits.shape = [batch size, sequence length, num of classes]\n",
    "        # labels.shape = [batch size, sequence length]\n",
    "        # padding_mask.shape = [batch size, sequence length]\n",
    "        bs, seq_len, nc = logits.shape\n",
    "        loss = F.cross_entropy(logits.reshape(bs * seq_len, nc), labels.reshape(-1), reduce=False, label_smoothing=self.label_smoothing) #label_smoothing表示随机将一个类别的概率设置为0.1，使得模型更加关注其他类别\n",
    "        if padding_mask is None:\n",
    "            loss = loss.mean()\n",
    "        else:\n",
    "            padding_mask = 1 - padding_mask.reshape(-1) #将padding_mask reshape成一维张量，mask部分为0，非mask部分为1\n",
    "            loss = torch.mul(loss, padding_mask).sum() / padding_mask.sum()\n",
    "\n",
    "        return loss\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "zuBC9KqFN4yG"
   },
   "source": [
    "### 学习率衰减"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 449
    },
    "id": "UQPiKK4nN4yG",
    "outputId": "632c2a03-7587-48cb-fb2e-159fb3e1bff9",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:33.201794900Z",
     "start_time": "2024-05-08T02:58:32.905395200Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/plain": "<Figure size 640x480 with 1 Axes>",
      "image/png": "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"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# NoamDecayScheduler 是一个自定义或外部定义的学习率衰减调度器类。它需要接收配置 config 作为参数，可能实现了特定的学习率衰减方案\n",
    "class NoamDecayScheduler:\n",
    "    def __init__(self, config):\n",
    "        self.d_model = config[\"d_model\"]\n",
    "        self.warmup_steps = config[\"warmup_steps\"]\n",
    "\n",
    "    def __call__(self, step):\n",
    "        step += 1\n",
    "        arg1 = step ** (-0.5)\n",
    "        arg2 = step * (self.warmup_steps ** (-1.5))\n",
    "\n",
    "        arg3 = self.d_model ** (-0.5)\n",
    "\n",
    "        return arg3 * np.minimum(arg1, arg2)\n",
    "\n",
    "\n",
    "temp_learning_rate_schedule = NoamDecayScheduler({\"d_model\": 512, \"warmup_steps\": 4000})\n",
    "#下面是学习率的设计图\n",
    "plt.plot(temp_learning_rate_schedule(np.arange(0, 40000)))\n",
    "plt.ylabel(\"Leraning rate\")\n",
    "plt.xlabel(\"Train step\")\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "XHj_EjzlN4yW"
   },
   "source": [
    "### 优化器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "id": "1EVLKx2rN4yW",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:33.219781400Z",
     "start_time": "2024-05-08T02:58:33.202792300Z"
    }
   },
   "outputs": [],
   "source": [
    "from torch.optim.lr_scheduler import LambdaLR\n",
    "from torch.optim import Adam\n",
    "\n",
    "def get_optimizer(model, config):\n",
    "    base_lr = 0.1\n",
    "    beta1 = config[\"beta1\"] # Adam 的 beta1\n",
    "    beta2 = config[\"beta2\"] # Adam 的 beta2\n",
    "    eps = config[\"eps\"]\n",
    "    optimizer = Adam(model.parameters(), lr=base_lr, betas=(beta1, beta2), eps=eps)\n",
    "    lr_scheduler = NoamDecayScheduler(config) #config是一个字典，包含了学习率衰减的参数\n",
    "    # 使用 LambdaLR 调度器，它可以根据给定的函数 lr_lambda 调整学习率。这里将 lr_scheduler 作为函数传递给 LambdaLR，它包含了特定于模型或任务的学习率调度规则\n",
    "    scheduler = LambdaLR(optimizer, lr_lambda=lr_scheduler)\n",
    "    return optimizer, scheduler"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "JBhiO7O2N4yW"
   },
   "source": [
    "### Callback"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "id": "rWFMJwBkN4yX",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:39.749917700Z",
     "start_time": "2024-05-08T02:58:33.219781400Z"
    }
   },
   "outputs": [],
   "source": [
    "from torch.utils.tensorboard import SummaryWriter\n",
    "\n",
    "\n",
    "class TensorBoardCallback:\n",
    "    def __init__(self, log_dir, flush_secs=10):\n",
    "        \"\"\"\n",
    "        Args:\n",
    "            log_dir (str): dir to write log.\n",
    "            flush_secs (int, optional): write to dsk each flush_secs seconds. Defaults to 10.\n",
    "        \"\"\"\n",
    "        self.writer = SummaryWriter(log_dir=log_dir, flush_secs=flush_secs)\n",
    "\n",
    "    def draw_model(self, model, input_shape):\n",
    "        self.writer.add_graph(model, input_to_model=torch.randn(input_shape))\n",
    "\n",
    "    def add_loss_scalars(self, step, loss, val_loss):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/loss\",\n",
    "            tag_scalar_dict={\"loss\": loss, \"val_loss\": val_loss},\n",
    "            global_step=step,\n",
    "            )\n",
    "\n",
    "    def add_acc_scalars(self, step, acc, val_acc):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/accuracy\",\n",
    "            tag_scalar_dict={\"accuracy\": acc, \"val_accuracy\": val_acc},\n",
    "            global_step=step,\n",
    "        )\n",
    "\n",
    "    def add_lr_scalars(self, step, learning_rate):\n",
    "        self.writer.add_scalars(\n",
    "            main_tag=\"training/learning_rate\",\n",
    "            tag_scalar_dict={\"learning_rate\": learning_rate},\n",
    "            global_step=step,\n",
    "\n",
    "        )\n",
    "\n",
    "    def __call__(self, step, **kwargs):\n",
    "        # add loss\n",
    "        loss = kwargs.pop(\"loss\", None)\n",
    "        val_loss = kwargs.pop(\"val_loss\", None)\n",
    "        if loss is not None and val_loss is not None:\n",
    "            self.add_loss_scalars(step, loss, val_loss)\n",
    "        # add acc\n",
    "        acc = kwargs.pop(\"acc\", None)\n",
    "        val_acc = kwargs.pop(\"val_acc\", None)\n",
    "        if acc is not None and val_acc is not None:\n",
    "            self.add_acc_scalars(step, acc, val_acc)\n",
    "        # add lr\n",
    "        learning_rate = kwargs.pop(\"lr\", None)\n",
    "        if learning_rate is not None:\n",
    "            self.add_lr_scalars(step, learning_rate)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "id": "64y_NBHMN4yX",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:39.767907500Z",
     "start_time": "2024-05-08T02:58:39.760912700Z"
    }
   },
   "outputs": [],
   "source": [
    "class SaveCheckpointsCallback:\n",
    "    def __init__(self, save_dir, save_step=5000, save_best_only=True):\n",
    "        \"\"\"\n",
    "        Save checkpoints each save_epoch epoch.\n",
    "        We save checkpoint by epoch in this implementation.\n",
    "        Usually, training scripts with pytorch evaluating model and save checkpoint by step.\n",
    "\n",
    "        Args:\n",
    "            save_dir (str): dir to save checkpoint\n",
    "            save_epoch (int, optional): the frequency to save checkpoint. Defaults to 1.\n",
    "            save_best_only (bool, optional): If True, only save the best model or save each model at every epoch.\n",
    "        \"\"\"\n",
    "        self.save_dir = save_dir\n",
    "        self.save_step = save_step\n",
    "        self.save_best_only = save_best_only\n",
    "        self.best_metrics = - np.inf\n",
    "\n",
    "        # mkdir\n",
    "        if not os.path.exists(self.save_dir):\n",
    "            os.mkdir(self.save_dir)\n",
    "\n",
    "    def __call__(self, step, state_dict, metric=None):\n",
    "        if step % self.save_step > 0:\n",
    "            return\n",
    "\n",
    "        if self.save_best_only:\n",
    "            assert metric is not None\n",
    "            if metric >= self.best_metrics:\n",
    "                # save checkpoints\n",
    "                torch.save(state_dict, os.path.join(self.save_dir, \"best.ckpt\"))\n",
    "                # update best metrics\n",
    "                self.best_metrics = metric\n",
    "        else:\n",
    "            torch.save(state_dict, os.path.join(self.save_dir, f\"{step}.ckpt\"))\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "id": "Uk4PEb70N4yX",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:39.806886900Z",
     "start_time": "2024-05-08T02:58:39.772906900Z"
    }
   },
   "outputs": [],
   "source": [
    "class EarlyStopCallback:\n",
    "    def __init__(self, patience=5, min_delta=0.01):\n",
    "        \"\"\"\n",
    "\n",
    "        Args:\n",
    "            patience (int, optional): Number of epochs with no improvement after which training will be stopped.. Defaults to 5.\n",
    "            min_delta (float, optional): Minimum change in the monitored quantity to qualify as an improvement, i.e. an absolute\n",
    "                change of less than min_delta, will count as no improvement. Defaults to 0.01.\n",
    "        \"\"\"\n",
    "        self.patience = patience\n",
    "        self.min_delta = min_delta\n",
    "        self.best_metric = - np.inf\n",
    "        self.counter = 0\n",
    "\n",
    "    def __call__(self, metric):\n",
    "        if metric >= self.best_metric + self.min_delta:\n",
    "            # update best metric\n",
    "            self.best_metric = metric\n",
    "            # reset counter\n",
    "            self.counter = 0\n",
    "        else:\n",
    "            self.counter += 1\n",
    "\n",
    "    @property\n",
    "    def early_stop(self):\n",
    "        return self.counter >= self.patience\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "_AB84Qx1N4yX"
   },
   "source": [
    "### training & valuating"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "id": "1mKPSFkON4yX",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:39.814884300Z",
     "start_time": "2024-05-08T02:58:39.803886800Z"
    }
   },
   "outputs": [],
   "source": [
    "@torch.no_grad()\n",
    "def evaluating(model, dataloader, loss_fct):\n",
    "    loss_list = []\n",
    "    for batch in dataloader:\n",
    "        encoder_inputs = batch[\"encoder_inputs\"]\n",
    "        encoder_inputs_mask = batch[\"encoder_inputs_mask\"]\n",
    "        decoder_inputs = batch[\"decoder_inputs\"]\n",
    "        decoder_labels = batch[\"decoder_labels\"]\n",
    "        decoder_labels_mask = batch[\"decoder_labels_mask\"]\n",
    "\n",
    "        # 前向计算\n",
    "        outputs = model(\n",
    "            encoder_inputs=encoder_inputs,\n",
    "            decoder_inputs=decoder_inputs,\n",
    "            encoder_inputs_mask=encoder_inputs_mask\n",
    "            )\n",
    "        logits = outputs.logits\n",
    "        loss = loss_fct(logits, decoder_labels, padding_mask=decoder_labels_mask)         # 验证集损失\n",
    "        loss_list.append(loss.cpu().item())\n",
    "\n",
    "    return np.mean(loss_list)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "id": "PClBmtgWN4yY",
    "ExecuteTime": {
     "end_time": "2024-05-08T02:58:39.826872600Z",
     "start_time": "2024-05-08T02:58:39.812882100Z"
    }
   },
   "outputs": [],
   "source": [
    "# 训练\n",
    "def training(\n",
    "    model,\n",
    "    train_loader,\n",
    "    val_loader,\n",
    "    epoch,\n",
    "    loss_fct,\n",
    "    optimizer,\n",
    "    scheduler=None,\n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=None,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500,\n",
    "    ):\n",
    "    record_dict = {\n",
    "        \"train\": [],\n",
    "        \"val\": []\n",
    "    }\n",
    "\n",
    "    global_step = 1\n",
    "    model.train()\n",
    "    with tqdm(total=epoch * len(train_loader)) as pbar:\n",
    "        for epoch_id in range(epoch):\n",
    "            # training\n",
    "            for batch in train_loader:\n",
    "                encoder_inputs = batch[\"encoder_inputs\"]\n",
    "                encoder_inputs_mask = batch[\"encoder_inputs_mask\"]\n",
    "                decoder_inputs = batch[\"decoder_inputs\"]\n",
    "                decoder_labels = batch[\"decoder_labels\"]\n",
    "                decoder_labels_mask = batch[\"decoder_labels_mask\"]\n",
    "                # 梯度清空\n",
    "                optimizer.zero_grad()\n",
    "\n",
    "                # 前向计算\n",
    "                outputs = model(\n",
    "                    encoder_inputs=encoder_inputs,\n",
    "                    decoder_inputs=decoder_inputs,\n",
    "                    encoder_inputs_mask=encoder_inputs_mask\n",
    "                    )\n",
    "                logits = outputs.logits\n",
    "                loss = loss_fct(logits, decoder_labels, padding_mask=decoder_labels_mask)\n",
    "\n",
    "                # 梯度回传\n",
    "                loss.backward()\n",
    "\n",
    "                # 调整优化器，包括学习率的变动等\n",
    "                optimizer.step()\n",
    "                if scheduler is not None:\n",
    "                    scheduler.step() # 更新学习率\n",
    "\n",
    "                loss = loss.cpu().item()\n",
    "                # record\n",
    "                record_dict[\"train\"].append({\n",
    "                    \"loss\": loss, \"step\": global_step\n",
    "                })\n",
    "\n",
    "                # evaluating\n",
    "                if global_step % eval_step == 0:\n",
    "                    model.eval()\n",
    "                    val_loss = evaluating(model, val_loader, loss_fct)\n",
    "                    record_dict[\"val\"].append({\n",
    "                        \"loss\": val_loss, \"step\": global_step\n",
    "                    })\n",
    "                    model.train()\n",
    "\n",
    "                    # 1. 使用 tensorboard 可视化\n",
    "                    cur_lr = optimizer.param_groups[0][\"lr\"] if scheduler is None else scheduler.get_last_lr()[0]\n",
    "                    if tensorboard_callback is not None:\n",
    "                        tensorboard_callback(\n",
    "                            global_step,\n",
    "                            loss=loss, val_loss=val_loss,\n",
    "                            lr=cur_lr,\n",
    "                            )\n",
    "\n",
    "                    # 2. 保存模型权重 save model checkpoint\n",
    "                    if save_ckpt_callback is not None:\n",
    "                        save_ckpt_callback(global_step, model.state_dict(), metric=-val_loss)\n",
    "\n",
    "                    # 3. 早停 Early Stop\n",
    "                    if early_stop_callback is not None:\n",
    "                        early_stop_callback(-val_loss)\n",
    "                        if early_stop_callback.early_stop:\n",
    "                            print(f\"Early stop at epoch {epoch_id} / global_step {global_step}\")\n",
    "                            return record_dict\n",
    "\n",
    "                # udate step\n",
    "                global_step += 1\n",
    "                pbar.update(1)\n",
    "            pbar.set_postfix({\"epoch\": epoch_id, \"loss\": loss, \"val_loss\": val_loss})\n",
    "\n",
    "    return record_dict\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "khZqMS8pN4yY",
    "outputId": "8cda6b76-e8af-4322-da0f-99daf4f7aeea",
    "ExecuteTime": {
     "end_time": "2024-05-07T08:52:34.996960Z",
     "start_time": "2024-05-07T08:52:34.662399200Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "load train dataset from wmt16\\.cache\\de2en_train_128.npy\n",
      "load val dataset from wmt16\\.cache\\de2en_val_128.npy\n"
     ]
    }
   ],
   "source": [
    "config = {\n",
    "    \"bos_idx\": 1,\n",
    "    \"eos_idx\": 3,\n",
    "    \"pad_idx\": 0,\n",
    "    \"vocab_size\": len(word2idx),\n",
    "    \"max_length\": 128,\n",
    "    \"d_model\": 512,\n",
    "    \"dim_feedforward\": 2048,\n",
    "    \"dropout\": 0.1,\n",
    "    \"layer_norm_eps\": 1e-6, # 层归一化的 epsilon, 防止除零错误\n",
    "    \"num_heads\": 8,\n",
    "    \"num_decoder_layers\": 6,\n",
    "    \"num_encoder_layers\": 6,\n",
    "    \"label_smoothing\": 0.1,\n",
    "    \"beta1\": 0.9,\n",
    "    \"beta2\": 0.98,\n",
    "    \"eps\": 1e-9,\n",
    "    \"warmup_steps\": 4_000,\n",
    "    \"share_embedding\": False, # 是否共享词向量\n",
    "    }\n",
    "\n",
    "\n",
    "def get_dl(dataset, batch_size, shuffle=True):\n",
    "    sampler = TransformerBatchSampler(dataset, batch_size=batch_size, shuffle_batch=shuffle)\n",
    "    sample_dl = DataLoader(dataset, batch_sampler=sampler, collate_fn=partial(collate_fct, tokenizer=tokenizer))\n",
    "    return sample_dl\n",
    "\n",
    "# dataset\n",
    "train_ds = LangPairDataset(\"train\", max_length=config[\"max_length\"])\n",
    "val_ds = LangPairDataset(\"val\", max_length=config[\"max_length\"])\n",
    "# tokenizer\n",
    "tokenizer = Tokenizer(word2idx=word2idx, idx2word=idx2word, max_length=config[\"max_length\"])\n",
    "batch_size = 2048\n",
    "# dataloader\n",
    "train_dl = get_dl(train_ds, batch_size=batch_size, shuffle=True)\n",
    "val_dl = get_dl(val_ds, batch_size=batch_size, shuffle=False)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "outputs": [
    {
     "data": {
      "text/plain": "{'bos_idx': 1,\n 'eos_idx': 3,\n 'pad_idx': 0,\n 'vocab_size': 9718,\n 'max_length': 128,\n 'd_model': 512,\n 'dim_feedforward': 2048,\n 'dropout': 0.1,\n 'layer_norm_eps': 1e-06,\n 'num_heads': 8,\n 'num_decoder_layers': 6,\n 'num_encoder_layers': 6,\n 'label_smoothing': 0.1,\n 'beta1': 0.9,\n 'beta2': 0.98,\n 'eps': 1e-09,\n 'warmup_steps': 4000,\n 'share_embedding': True}"
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "config"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-05-07T08:52:38.372364400Z",
     "start_time": "2024-05-07T08:52:38.191467100Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 104,
     "referenced_widgets": [
      "71828b81a9ef4fd18afd4d068f78dcdf",
      "b2ef21b26fcf45b48bb9820a63c4b085",
      "561a4754313944a09cee83a437ffe378",
      "30218102d2934032a7123b54a374d561",
      "adacd550ea0f49afa00875767a4ed2e9",
      "8cff93cd0f4e45729ad26506b8b47529",
      "f8b25d02c9234a3c97392d5f8c43bfc6",
      "eb60738f29a646b28a34311186e2d722",
      "f5fbc7cda82040738f883220b93cfb00",
      "cd3e43fc4c6c445a97982adcce349f2d",
      "c542449b28e349f79e9b14b0f69c7c41"
     ]
    },
    "id": "tVO2zJ04N4yY",
    "outputId": "534b7b08-125a-40e8-ccb9-8d36bb2fd028"
   },
   "outputs": [
    {
     "output_type": "display_data",
     "data": {
      "text/plain": [
       "  0%|          | 0/280 [00:00<?, ?it/s]"
      ],
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "71828b81a9ef4fd18afd4d068f78dcdf"
      }
     },
     "metadata": {}
    },
    {
     "output_type": "stream",
     "name": "stderr",
     "text": [
      "/usr/local/lib/python3.10/dist-packages/torch/nn/_reduction.py:42: UserWarning: size_average and reduce args will be deprecated, please use reduction='none' instead.\n",
      "  warnings.warn(warning.format(ret))\n"
     ]
    }
   ],
   "source": [
    "epoch = 20\n",
    "\n",
    "# model\n",
    "model = TransformerModel(config)\n",
    "# 1. 定义损失函数 采用交叉熵损失\n",
    "loss_fct = CrossEntropyWithPadding(config)\n",
    "# 2. 定义优化器 采用 adam\n",
    "# Optimizers specified in the torch.optim package\n",
    "optimizer, scheduler = get_optimizer(model, config)\n",
    "\n",
    "# 1. tensorboard 可视化\n",
    "if not os.path.exists(\"runs\"):\n",
    "    os.mkdir(\"runs\")\n",
    "exp_name = \"translate-transformer-{}\".format(\"share\" if config[\"share_embedding\"] else \"not-share\")\n",
    "tensorboard_callback = TensorBoardCallback(f\"runs/{exp_name}\")\n",
    "# tensorboard_callback.draw_model(model, [1, MAX_LENGTH])\n",
    "# 2. save best\n",
    "if not os.path.exists(\"checkpoints\"):\n",
    "    os.makedirs(\"checkpoints\")\n",
    "save_ckpt_callback = SaveCheckpointsCallback(\n",
    "    f\"checkpoints/{exp_name}\", save_step=500, save_best_only=True)\n",
    "# 3. early stop\n",
    "early_stop_callback = EarlyStopCallback(patience=8)\n",
    "\n",
    "model = model.to(device)\n",
    "\n",
    "record = training(\n",
    "    model,\n",
    "    train_dl,\n",
    "    val_dl,\n",
    "    epoch,\n",
    "    loss_fct,\n",
    "    optimizer,\n",
    "    scheduler,\n",
    "    tensorboard_callback=None,\n",
    "    save_ckpt_callback=save_ckpt_callback,\n",
    "    early_stop_callback=None,\n",
    "    eval_step=500\n",
    "    )\n",
    "\n",
    "# Training took 3.5 days on 8 P100 GPUs\n",
    "# We trained the base models for a total of 100,000 steps or 12 hours. For our big models,(described on the bottom line of table 3), step time was 1.0 seconds. The big models were trained for 300,000 steps (3.5 days)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "id": "rTjbeFlIN4yZ"
   },
   "source": [
    "## 推理\n",
    "\n",
    "- 翻译项目的评估指标一般是BLEU4，感兴趣的同学自行了解并实现\n",
    "- 接下来进行翻译推理，并作出注意力的热度图"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "WxsjkyQgN4yZ",
    "outputId": "313bcaea-0de9-4e18-92ca-14ad820329d8"
   },
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "Collecting fastBPE\n",
      "  Downloading fastBPE-0.1.0.tar.gz (35 kB)\n",
      "  Preparing metadata (setup.py) ... \u001B[?25l\u001B[?25hdone\n",
      "Building wheels for collected packages: fastBPE\n",
      "  Building wheel for fastBPE (setup.py) ... \u001B[?25l\u001B[?25hdone\n",
      "  Created wheel for fastBPE: filename=fastBPE-0.1.0-cp310-cp310-linux_x86_64.whl size=806590 sha256=d657294201476d9e9e4178949c572d486c2b90e5d20a0f254cdb6a7f158b5f67\n",
      "  Stored in directory: /root/.cache/pip/wheels/13/5d/b9/4b8897941ebc9e8c6cc3f3ffd3ea5115731754269205098754\n",
      "Successfully built fastBPE\n",
      "Installing collected packages: fastBPE\n",
      "Successfully installed fastBPE-0.1.0\n"
     ]
    }
   ],
   "source": [
    "# !pip install Cython  # if failed to install fastBPE, try this line\n",
    "!pip install fastBPE #分词使用\n",
    "# 在 Windows 系统上并没有 sys/mman.h 文件"
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "exp_name"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 35
    },
    "id": "f6Ry1PcAn4k1",
    "outputId": "8d1dfc32-aa1e-44b1-ede7-c00d79a43e0e"
   },
   "execution_count": 34,
   "outputs": [
    {
     "output_type": "execute_result",
     "data": {
      "text/plain": [
       "'translate-transformer-share'"
      ],
      "application/vnd.google.colaboratory.intrinsic+json": {
       "type": "string"
      }
     },
     "metadata": {},
     "execution_count": 34
    }
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "id": "jT1Mqiq3N4yZ",
    "ExecuteTime": {
     "end_time": "2024-05-06T12:53:10.207571800Z",
     "start_time": "2024-05-06T12:53:09.743836600Z"
    }
   },
   "outputs": [],
   "source": [
    "import torch\n",
    "\n",
    "# state_dict = torch.load(f\"checkpoints/{exp_name}/best.ckpt\", map_location=\"cpu\")\n",
    "\n",
    "state_dict1 = torch.load(\"epoch125-step132426.ckpt\", map_location=\"cpu\")\n",
    "state_dict = state_dict1[\"state_dict\"]\n",
    "\n",
    "# update keys by dropping `model`\n",
    "for key in list(state_dict):\n",
    "    state_dict[key.replace(\"model.\", \"\")] = state_dict.pop(key)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "outputs": [
    {
     "data": {
      "text/plain": "['a man in an orange hat starring at something .']"
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tokenizer.decode([[   5,   16,    6,   23,  150,   80, 8248,   35,  232,    4,    3]])"
   ],
   "metadata": {
    "collapsed": false,
    "ExecuteTime": {
     "end_time": "2024-05-08T13:03:21.551891700Z",
     "start_time": "2024-05-08T13:03:21.527908700Z"
    }
   }
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 84,
     "referenced_widgets": [
      "7670a7b7461340209e36c72b8aeb74d4",
      "17ba1ca7806846b9a3e53450f7169b5c",
      "8140d1c1cf954429aee4c0e93f02cdf2",
      "8931b71471444523a13525e074c9c41a",
      "b15071ef569c46d9bb59e00f03fb1f86",
      "e8ae90a9c1984ee3ae4f66bc01d6b366",
      "498fbbd091cf48228a0aef0505acc5ea",
      "5fa0e7a87dee48098a6a15344f04ddc5",
      "267e97b25ebc4f98a0df7484035c2195",
      "07f23ff4182d4e93b37542f6d068d9f6",
      "7499f7f692e74a4fa6dba0711fa15311"
     ]
    },
    "id": "i_0CPyDZN4ya",
    "outputId": "d27b6b0d-d31d-411f-820e-1428489d2880",
    "ExecuteTime": {
     "end_time": "2024-05-06T12:53:13.024978300Z",
     "start_time": "2024-05-06T12:53:10.213569700Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "save cache to wmt16\\.cache\\de2en_test_128.npy\n"
     ]
    },
    {
     "data": {
      "text/plain": "0it [00:00, ?it/s]",
      "application/vnd.jupyter.widget-view+json": {
       "version_major": 2,
       "version_minor": 0,
       "model_id": "5ef9f1b1e1014afc981660b98db2a457"
      }
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "ename": "NameError",
     "evalue": "name 'loss_fct' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[1;31mNameError\u001B[0m                                 Traceback (most recent call last)",
      "Cell \u001B[1;32mIn[37], line 26\u001B[0m\n\u001B[0;32m     20\u001B[0m \u001B[38;5;66;03m# 前向计算\u001B[39;00m\n\u001B[0;32m     21\u001B[0m outputs \u001B[38;5;241m=\u001B[39m model(\n\u001B[0;32m     22\u001B[0m     encoder_inputs\u001B[38;5;241m=\u001B[39mencoder_inputs,\n\u001B[0;32m     23\u001B[0m     decoder_inputs\u001B[38;5;241m=\u001B[39mdecoder_inputs,\n\u001B[0;32m     24\u001B[0m     encoder_inputs_mask\u001B[38;5;241m=\u001B[39mencoder_inputs_mask\n\u001B[0;32m     25\u001B[0m     )\n\u001B[1;32m---> 26\u001B[0m loss \u001B[38;5;241m=\u001B[39m \u001B[43mloss_fct\u001B[49m(outputs\u001B[38;5;241m.\u001B[39mlogits, decoder_labels)         \u001B[38;5;66;03m# 验证集损失\u001B[39;00m\n\u001B[0;32m     27\u001B[0m \u001B[38;5;66;03m# outputs = model.infer(encoder_inputs=encoder_inputs)\u001B[39;00m\n\u001B[0;32m     28\u001B[0m \u001B[38;5;66;03m# print(outputs.logits.shape, decoder_labels.shape)\u001B[39;00m\n\u001B[0;32m     29\u001B[0m \n\u001B[0;32m     30\u001B[0m \u001B[38;5;66;03m# loss = loss_fct(outputs.logits[:, :decoder_labels.shape[1]], decoder_labels)         # 验证集损失\u001B[39;00m\n\u001B[0;32m     31\u001B[0m \u001B[38;5;66;03m# preds = logits.argmax(dim=-1)\u001B[39;00m\n\u001B[0;32m     32\u001B[0m collect[idx] \u001B[38;5;241m=\u001B[39m {\u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mloss\u001B[39m\u001B[38;5;124m\"\u001B[39m: loss\u001B[38;5;241m.\u001B[39mitem(), \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124msrc_inputs\u001B[39m\u001B[38;5;124m\"\u001B[39m: encoder_inputs, \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mtrg_inputs\u001B[39m\u001B[38;5;124m\"\u001B[39m: decoder_inputs, \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mmask\u001B[39m\u001B[38;5;124m\"\u001B[39m: encoder_inputs_mask, \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mtrg_labels\u001B[39m\u001B[38;5;124m\"\u001B[39m: decoder_labels, \u001B[38;5;124m\"\u001B[39m\u001B[38;5;124mpreds\u001B[39m\u001B[38;5;124m\"\u001B[39m: outputs\u001B[38;5;241m.\u001B[39mpreds}\n",
      "\u001B[1;31mNameError\u001B[0m: name 'loss_fct' is not defined"
     ]
    }
   ],
   "source": [
    "# load checkpoints\n",
    "model = TransformerModel(config)\n",
    "model.load_state_dict(state_dict)\n",
    "\n",
    "\n",
    "# from dataset import LangPairDataset\n",
    "test_ds = LangPairDataset(\"test\", max_length=128, data_dir=\"./wmt16\")\n",
    "test_dl = DataLoader(test_ds, batch_size=1, collate_fn=partial(collate_fct, tokenizer=tokenizer))\n",
    "\n",
    "model = model.to(device)\n",
    "model.eval()\n",
    "collect = {}\n",
    "loss_collect = []\n",
    "from nltk.translate.bleu_score import sentence_bleu\n",
    "predictions = []\n",
    "answers = []\n",
    "# 初始化BLEU分数列表\n",
    "bleu_scores = []\n",
    "for idx, batch in tqdm(enumerate(test_dl)):\n",
    "    encoder_inputs = batch[\"encoder_inputs\"].to(device=device)\n",
    "    encoder_inputs_mask = batch[\"encoder_inputs_mask\"].to(device=device)\n",
    "    decoder_inputs = batch[\"decoder_inputs\"].to(device=device)\n",
    "    decoder_labels = batch[\"decoder_labels\"].to(device=device)\n",
    "\n",
    "    # 前向计算\n",
    "    outputs = model(\n",
    "        encoder_inputs=encoder_inputs,\n",
    "        decoder_inputs=decoder_inputs,\n",
    "        encoder_inputs_mask=encoder_inputs_mask\n",
    "        )\n",
    "    loss = loss_fct(outputs.logits, decoder_labels)         # 验证集损失\n",
    "\n",
    "    # print(outputs.logits.shape, decoder_labels.shape)\n",
    "\n",
    "    # loss = loss_fct(outputs.logits[:, :decoder_labels.shape[1]], decoder_labels)         # 验证集损失\n",
    "    outputs = model.infer(encoder_inputs=encoder_inputs)\n",
    "    preds = outputs.logits.argmax(dim=-1) # 预测结果,shape [1, seq_len]\n",
    "    #把preds转为英文单词\n",
    "    preds = tokenizer.decode(preds)\n",
    "    # predictions.append(preds)\n",
    "    #把decoder_labels转为英文单词\n",
    "    decoder_labels = tokenizer.decode(decoder_labels)\n",
    "    # answers.append(decoder_labels)\n",
    "    belu=sentence_bleu(decoder_labels.split(),preds.split(),weights=(0.25, 0.25, 0.25, 0.25))\n",
    "    bleu_scores.append(belu)\n",
    "    collect[idx] = {\"loss\": loss.item(), \"src_inputs\": encoder_inputs, \"trg_inputs\": decoder_inputs, \"mask\": encoder_inputs_mask, \"trg_labels\": decoder_labels, \"preds\": preds}\n",
    "    loss_collect.append(loss.item())\n",
    "\n",
    "# sort collect by value\n",
    "collect = sorted(collect.items(), key=lambda x: x[1][\"loss\"])\n",
    "print(f\"testing loss: {np.array(loss_collect).mean()}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 1000,
     "referenced_widgets": [
      "9238dc0e0c6746aa9bf2f94c79165b53",
      "89e07f16fd1448febc160dc649377478",
      "1f47a72210494bd4ac5c0027d1fa0365",
      "b47cc34a13c04bc180206e12ff2d0687",
      "7aae2717cd1d446a84e49ea79c239f77",
      "96c5299b320147be8223c42a0cfc964d",
      "513127241a1640b08c7cd4fa8aba10ac",
      "f31eea249e52412fa4751cf11ddfba86",
      "539d82b8cbb4448fbb2e80e87d4eaf84",
      "b1906c727a9148d5af7ff91bb2ef7da8",
      "4c4b81615aef4f018b82f6a23a2ab822"
     ]
    },
    "id": "KGSym4CbN4ya",
    "outputId": "d605e7e0-6e52-4155-8740-8fbe59277119",
    "ExecuteTime": {
     "end_time": "2024-05-06T13:16:14.099749Z",
     "start_time": "2024-05-06T13:16:13.991811900Z"
    }
   },
   "outputs": [
    {
     "ename": "ModuleNotFoundError",
     "evalue": "No module named 'fastBPE'",
     "output_type": "error",
     "traceback": [
      "\u001B[1;31m---------------------------------------------------------------------------\u001B[0m",
      "\u001B[1;31mModuleNotFoundError\u001B[0m                       Traceback (most recent call last)",
      "Cell \u001B[1;32mIn[38], line 2\u001B[0m\n\u001B[0;32m      1\u001B[0m \u001B[38;5;28;01mimport\u001B[39;00m \u001B[38;5;21;01mre\u001B[39;00m\n\u001B[1;32m----> 2\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01mfastBPE\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m fastBPE\n\u001B[0;32m      3\u001B[0m \u001B[38;5;28;01mfrom\u001B[39;00m \u001B[38;5;21;01msacremoses\u001B[39;00m \u001B[38;5;28;01mimport\u001B[39;00m MosesDetokenizer, MosesTokenizer\n\u001B[0;32m      5\u001B[0m \u001B[38;5;66;03m# `MosesTokenizer` 和 `MosesDetokenizer` 是来自 `sacremoses` 库的工具，用于自然语言处理中的分词（Tokenization）和去标记化（Detokenization）。这些工具主要用于对文本进行预处理和后处理，通常在处理自然语言处理任务时会用到。\u001B[39;00m\n\u001B[0;32m      6\u001B[0m \u001B[38;5;66;03m#\u001B[39;00m\n\u001B[0;32m      7\u001B[0m \u001B[38;5;66;03m# ### MosesTokenizer：\u001B[39;00m\n\u001B[1;32m   (...)\u001B[0m\n\u001B[0;32m     21\u001B[0m \u001B[38;5;66;03m#\u001B[39;00m\n\u001B[0;32m     22\u001B[0m \u001B[38;5;66;03m# 这些工具通常在文本预处理和后处理过程中使用，对输入的文本进行标记化和去标记化，是一种常用的处理方式。在自然语言处理任务中，对文本进行正确的分词和还原是很重要的，而 `MosesTokenizer` 和 `MosesDetokenizer` 提供了方便、高效的工具来处理这些任务。\u001B[39;00m\n",
      "\u001B[1;31mModuleNotFoundError\u001B[0m: No module named 'fastBPE'"
     ]
    }
   ],
   "source": [
    "import re\n",
    "from fastBPE import fastBPE\n",
    "from sacremoses import MosesDetokenizer, MosesTokenizer\n",
    "\n",
    "# `MosesTokenizer` 和 `MosesDetokenizer` 是来自 `sacremoses` 库的工具，用于自然语言处理中的分词（Tokenization）和去标记化（Detokenization）。这些工具主要用于对文本进行预处理和后处理，通常在处理自然语言处理任务时会用到。\n",
    "#\n",
    "# ### MosesTokenizer：\n",
    "# - **作用**：将原始文本分割成单词和标点符号。\n",
    "# - **特点**：基于 Moses 翻译工具中使用的分词方法。\n",
    "# - **功能**：\n",
    "#   - 将句子分割成单词和标点符号。\n",
    "#   - 处理缩写、连字符、标点等特殊情况。\n",
    "#   - 对文本进行标记化，方便后续处理。\n",
    "#\n",
    "# ### MosesDetokenizer：\n",
    "# - **作用**：将分词后的文本重新组合成原始的句子。\n",
    "# - **特点**：用于对分词后的文本进行还原，使其恢复为可读的句子形式。\n",
    "# - **功能**：\n",
    "#   - 将分词后的单词和标点符号重新组合成句子。\n",
    "#   - 处理分词后的标点、缩写等情况，使得结果更加自然和可读。\n",
    "#\n",
    "# 这些工具通常在文本预处理和后处理过程中使用，对输入的文本进行标记化和去标记化，是一种常用的处理方式。在自然语言处理任务中，对文本进行正确的分词和还原是很重要的，而 `MosesTokenizer` 和 `MosesDetokenizer` 提供了方便、高效的工具来处理这些任务。\n",
    "\n",
    "class Translator:\n",
    "    def __init__(self, model, src_tokenizer, trg_tokenizer):\n",
    "        self.bpe = fastBPE(\"./wmt16/bpe.10000\", \"./wmt16/vocab\")\n",
    "        self.mose_tokenizer = MosesTokenizer(lang=\"de\")\n",
    "        self.mose_detokenizer = MosesDetokenizer(lang=\"en\")\n",
    "        self.model = model\n",
    "        self.model.eval()\n",
    "        self.src_tokenizer = src_tokenizer\n",
    "        self.trg_tokenizer = trg_tokenizer\n",
    "        self.pattern = re.compile(r'(@@ )|(@@ ?$)')\n",
    "\n",
    "    def draw_attention_map(self, attn_scores, cross_attn_scores, src_words_list, trg_words_list):\n",
    "        \"\"\"绘制注意力热力图\n",
    "        attn_scores (numpy.ndarray): 表示自注意力机制（self-attention）分数。\n",
    "        cross_attn_scores (numpy.ndarray): 表示交叉注意力机制的注意力分数。\n",
    "        src_words_list (list): 源语言句子的单词列表。\n",
    "        trg_words_list (list): 目标语言句子的单词列表。\n",
    "        \"\"\"\n",
    "        assert len(attn_scores.shape) == 3, \"attn_scores shape should be \" \\\n",
    "            f\"[num heads, target sequence length, target sequence length], but got {attn_scores.shape}\"\n",
    "        attn_scores = attn_scores[:, :len(trg_words_list), :len(trg_words_list)]\n",
    "\n",
    "        assert len(cross_attn_scores.shape) == 3, \"attn_scores shape should be \" \\\n",
    "            f\"[num heads, target sequence length, source sequence length], but got {cross_attn_scores.shape}\"\n",
    "        cross_attn_scores = cross_attn_scores[:, :len(trg_words_list), :len(src_words_list)]\n",
    "\n",
    "        num_heads, trg_len, src_len = cross_attn_scores.shape\n",
    "\n",
    "        fig = plt.figure(figsize=(10, 5), constrained_layout=True) # constrained_layout=True 自动调整子图参数，使之填充整个图像区域\n",
    "        grid = plt.GridSpec(trg_len, trg_len + src_len, wspace=0.1, hspace=0.1)# wspace,hspace 控制子图之间的间距\n",
    "        #下面是attn_scores的热力图\n",
    "        self_map = fig.add_subplot(grid[:,:trg_len]) #  添加子图\n",
    "        self_map.matshow(attn_scores.mean(dim=0), cmap='viridis') # 绘制热力图，cmap表示颜色,dim=0表示对第0维求均值\n",
    "        self_map.set_yticks(range(trg_len), trg_words_list, fontsize=10)\n",
    "        self_map.set_xticks(range(trg_len), [\"[BOS]\"] + trg_words_list[:-1], rotation=90)\n",
    "        #下面是cross_attn_scores的热力图\n",
    "        cross_map = fig.add_subplot(grid[:, trg_len:])\n",
    "        cross_map.matshow(cross_attn_scores.mean(dim=0), cmap='viridis')\n",
    "        cross_map.set_yticks(range(trg_len), [], fontsize=6)\n",
    "        cross_map.set_xticks(range(src_len), src_words_list, rotation=90)\n",
    "\n",
    "        plt.show()\n",
    "\n",
    "    def draw_attention_maps(self, attn_scores, cross_attn_scores, src_words_list, trg_words_list, heads_list):\n",
    "        \"\"\"绘制注意力热力图\n",
    "\n",
    "        Args:\n",
    "            - scores (numpy.ndarray): shape = [source sequence length, target sequence length]\n",
    "        \"\"\"\n",
    "        assert len(attn_scores.shape) == 3, \"attn_scores shape should be \" \\\n",
    "            f\"[num heads, target sequence length, target sequence length], but got {attn_scores.shape}\"\n",
    "        attn_scores = attn_scores[:, :len(trg_words_list), :len(trg_words_list)]\n",
    "\n",
    "        assert len(cross_attn_scores.shape) == 3, \"attn_scores shape should be \" \\\n",
    "            f\"[num heads, target sequence length, source sequence length], but got {cross_attn_scores.shape}\"\n",
    "        cross_attn_scores = cross_attn_scores[:, :len(trg_words_list), :len(src_words_list)]\n",
    "        # cross_attn_scores = cross_attn_scores[:, :len(src_words_list), :len(src_words_list)]\n",
    "\n",
    "        num_heads, trg_len, src_len = cross_attn_scores.shape\n",
    "        fig, axes = plt.subplots(2, len(heads_list), figsize=(5 * len(heads_list), 10))\n",
    "        for i, heads_idx in enumerate(heads_list):\n",
    "            axes[0, i].matshow(attn_scores[heads_idx], cmap='viridis')\n",
    "            axes[0, i].set_yticks(range(trg_len), trg_words_list)\n",
    "            axes[0, i].set_xticks(range(trg_len), [\"[BOS]\"] + trg_words_list[:-1], rotation=90)\n",
    "            axes[0, i].set_title(f\"head {heads_idx}\")\n",
    "            axes[1, i].matshow(cross_attn_scores[heads_idx], cmap='viridis')\n",
    "            axes[1, i].set_yticks(range(trg_len), trg_words_list)\n",
    "            axes[1, i].set_xticks(range(src_len), src_words_list, rotation=90)\n",
    "            axes[1, i].set_title(f\"head {heads_idx}\")\n",
    "\n",
    "        plt.show()\n",
    "\n",
    "\n",
    "    def __call__(self, sentence_list, heads_list=None, layer_idx=-1):\n",
    "        # 将输入句子列表转换为小写，并使用 MosesTokenizer 进行分词处理。\n",
    "        sentence_list = [\" \".join(self.mose_tokenizer.tokenize(s.lower())) for s in sentence_list]\n",
    "        # 将分词后的结果进行 BPE 编码，得到 tokens_list。\n",
    "        tokens_list = [s.split() for s in self.bpe.apply(sentence_list)]\n",
    "        # 使用 src_tokenizer 对 tokens_list 进行编码，同时添加起始标记 ([BOS]) 和结束标记 ([EOS])。\n",
    "        encoder_input, attn_mask = self.src_tokenizer.encode(\n",
    "            tokens_list,\n",
    "            add_bos=True,\n",
    "            add_eos=True,\n",
    "            return_mask=True,\n",
    "            )\n",
    "        encoder_input = torch.Tensor(encoder_input).to(dtype=torch.int64)\n",
    "        # 使用模型的 infer 方法对编码器输入进行推理，得到输出结果 outputs\n",
    "        outputs = model.infer(encoder_inputs=encoder_input, encoder_inputs_mask=attn_mask)\n",
    "\n",
    "        preds = outputs.preds.numpy()\n",
    "        # 使用目标语言的 trg_tokenizer 对预测序列进行解码，得到解码后的目标语言句子列表 trg_decoded。\n",
    "        trg_decoded = self.trg_tokenizer.decode(preds, split=True, remove_eos=False, remove_bos=False, remove_pad=False)\n",
    "        # 使用源语言的 src_tokenizer 对编码器输入进行解码，得到解码后的源语言句子列表 src_decoded。为下面绘制热力图做准备。\n",
    "        src_decoded = self.src_tokenizer.decode(\n",
    "            encoder_input.numpy(),\n",
    "            split=True,\n",
    "            remove_bos=False,\n",
    "            remove_eos=False\n",
    "            )\n",
    "\n",
    "        # post processed attn scores\n",
    "        # outputs.decoder_attentions[-1]  # the last layer of self-attention scores\n",
    "\n",
    "        # draw the attention map of the last decoder block\n",
    "        for attn_score, cross_attn_score, src, trg in zip(\n",
    "            outputs.decoder_self_attn_scores[layer_idx], outputs.decoder_cross_attn_scores[layer_idx], src_decoded, trg_decoded):\n",
    "            if heads_list is None:# 如果没有指定heads_list，就画单个热力图\n",
    "                self.draw_attention_map(\n",
    "                    attn_score,\n",
    "                    cross_attn_score,\n",
    "                    src,\n",
    "                    trg,\n",
    "                )\n",
    "            else:# 如果指定了heads_list，就画多个热力图\n",
    "                self.draw_attention_maps(\n",
    "                    attn_score,\n",
    "                    cross_attn_score,\n",
    "                    src,\n",
    "                    trg,\n",
    "                    heads_list=heads_list,\n",
    "                    )\n",
    "        return [self.mose_detokenizer.tokenize(self.pattern.sub(\"\", s).split()) for s in self.trg_tokenizer.decode(preds)] #将解码后的目标语言句子列表返回，并使用 mose_detokenizer 进行去标记化，最终得到翻译后的结果。\n",
    "\n",
    "\n",
    "sentence_list = [\n",
    "    \"Mann in einem kleinen weißen Boot auf einem See.\",  # Man in a small white boat on a lake.\n",
    "    \"Ein Mann mit einem Eimer und ein Mädchen mit einem Hut am Strand.\", # A man with a bucket and a girl in a hat on the beach.\n",
    "    \"Drei Männer auf Pferden während eines Rennens.\",  # Three men on horses during a race.\n",
    "    \"Ein Mann und eine Frau essen zu Abend\",  # 一个男人和一个女人在吃晚餐\n",
    "]\n",
    "\n",
    "\n",
    "# load checkpoints\n",
    "model = TransformerModel(config)\n",
    "model.load_state_dict(state_dict)\n",
    "translator = Translator(model.cpu(), tokenizer, tokenizer)\n",
    "translator(\n",
    "    sentence_list,\n",
    "    layer_idx=-1,\n",
    "    # heads_list=[0, 1, 2, 3, 4, 5, 6, 7]\n",
    "    )\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "name": "python3",
   "language": "python",
   "display_name": "Python 3 (ipykernel)"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.8"
  },
  "orig_nbformat": 4,
  "colab": {
   "provenance": [],
   "gpuType": "L4"
  },
  "accelerator": "GPU",
  "widgets": {
   "application/vnd.jupyter.widget-state+json": {
    "2c39e7db4e4d4ff9b0a1e9dfaf84203a": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HBoxModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HBoxModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HBoxView",
      "box_style": "",
      "children": [
       "IPY_MODEL_44d3be9cd000455dac8a0f8276a44f0e",
       "IPY_MODEL_8c382bf77f9c496db2701139d7cb5540",
       "IPY_MODEL_84f10bc140954a29b67af155bab4152b"
      ],
      "layout": "IPY_MODEL_0b244b586d5d410c882afe33731462bc"
     }
    },
    "44d3be9cd000455dac8a0f8276a44f0e": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_941a63e0eeab48608360eba3ff031455",
      "placeholder": "​",
      "style": "IPY_MODEL_fcba3fc7e93747cdb56c59ea495fe50e",
      "value": "100%"
     }
    },
    "8c382bf77f9c496db2701139d7cb5540": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "FloatProgressModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "FloatProgressModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "ProgressView",
      "bar_style": "success",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_92cce96ece7d49a5bf92ab69d3bd488c",
      "max": 9714,
      "min": 0,
      "orientation": "horizontal",
      "style": "IPY_MODEL_534c330eaa1c46f99d8da7365bca9253",
      "value": 9714
     }
    },
    "84f10bc140954a29b67af155bab4152b": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_0fc0bd90e8cf45a9bb1555f11084e6fe",
      "placeholder": "​",
      "style": "IPY_MODEL_38d606358679482bb9b5ec8029b91cb1",
      "value": " 9714/9714 [00:00&lt;00:00, 411267.71it/s]"
     }
    },
    "0b244b586d5d410c882afe33731462bc": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "941a63e0eeab48608360eba3ff031455": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "fcba3fc7e93747cdb56c59ea495fe50e": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "92cce96ece7d49a5bf92ab69d3bd488c": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "534c330eaa1c46f99d8da7365bca9253": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "ProgressStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "ProgressStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "bar_color": null,
      "description_width": ""
     }
    },
    "0fc0bd90e8cf45a9bb1555f11084e6fe": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "38d606358679482bb9b5ec8029b91cb1": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "71828b81a9ef4fd18afd4d068f78dcdf": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HBoxModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HBoxModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HBoxView",
      "box_style": "",
      "children": [
       "IPY_MODEL_b2ef21b26fcf45b48bb9820a63c4b085",
       "IPY_MODEL_561a4754313944a09cee83a437ffe378",
       "IPY_MODEL_30218102d2934032a7123b54a374d561"
      ],
      "layout": "IPY_MODEL_adacd550ea0f49afa00875767a4ed2e9"
     }
    },
    "b2ef21b26fcf45b48bb9820a63c4b085": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_8cff93cd0f4e45729ad26506b8b47529",
      "placeholder": "​",
      "style": "IPY_MODEL_f8b25d02c9234a3c97392d5f8c43bfc6",
      "value": ""
     }
    },
    "561a4754313944a09cee83a437ffe378": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "FloatProgressModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "FloatProgressModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "ProgressView",
      "bar_style": "success",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_eb60738f29a646b28a34311186e2d722",
      "max": 280,
      "min": 0,
      "orientation": "horizontal",
      "style": "IPY_MODEL_f5fbc7cda82040738f883220b93cfb00",
      "value": 280
     }
    },
    "30218102d2934032a7123b54a374d561": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_cd3e43fc4c6c445a97982adcce349f2d",
      "placeholder": "​",
      "style": "IPY_MODEL_c542449b28e349f79e9b14b0f69c7c41",
      "value": " 21020/? [19:42&lt;00:00, 16.67it/s, epoch=19, loss=2.41, val_loss=2.93]"
     }
    },
    "adacd550ea0f49afa00875767a4ed2e9": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "8cff93cd0f4e45729ad26506b8b47529": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "f8b25d02c9234a3c97392d5f8c43bfc6": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "eb60738f29a646b28a34311186e2d722": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "f5fbc7cda82040738f883220b93cfb00": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "ProgressStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "ProgressStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "bar_color": null,
      "description_width": ""
     }
    },
    "cd3e43fc4c6c445a97982adcce349f2d": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "c542449b28e349f79e9b14b0f69c7c41": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "7670a7b7461340209e36c72b8aeb74d4": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HBoxModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HBoxModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HBoxView",
      "box_style": "",
      "children": [
       "IPY_MODEL_17ba1ca7806846b9a3e53450f7169b5c",
       "IPY_MODEL_8140d1c1cf954429aee4c0e93f02cdf2",
       "IPY_MODEL_8931b71471444523a13525e074c9c41a"
      ],
      "layout": "IPY_MODEL_b15071ef569c46d9bb59e00f03fb1f86"
     }
    },
    "17ba1ca7806846b9a3e53450f7169b5c": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_e8ae90a9c1984ee3ae4f66bc01d6b366",
      "placeholder": "​",
      "style": "IPY_MODEL_498fbbd091cf48228a0aef0505acc5ea",
      "value": ""
     }
    },
    "8140d1c1cf954429aee4c0e93f02cdf2": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "FloatProgressModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "FloatProgressModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "ProgressView",
      "bar_style": "success",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_5fa0e7a87dee48098a6a15344f04ddc5",
      "max": 1,
      "min": 0,
      "orientation": "horizontal",
      "style": "IPY_MODEL_267e97b25ebc4f98a0df7484035c2195",
      "value": 1
     }
    },
    "8931b71471444523a13525e074c9c41a": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_07f23ff4182d4e93b37542f6d068d9f6",
      "placeholder": "​",
      "style": "IPY_MODEL_7499f7f692e74a4fa6dba0711fa15311",
      "value": " 957/? [00:15&lt;00:00, 63.19it/s]"
     }
    },
    "b15071ef569c46d9bb59e00f03fb1f86": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "e8ae90a9c1984ee3ae4f66bc01d6b366": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "498fbbd091cf48228a0aef0505acc5ea": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "5fa0e7a87dee48098a6a15344f04ddc5": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": "20px"
     }
    },
    "267e97b25ebc4f98a0df7484035c2195": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "ProgressStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "ProgressStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "bar_color": null,
      "description_width": ""
     }
    },
    "07f23ff4182d4e93b37542f6d068d9f6": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "7499f7f692e74a4fa6dba0711fa15311": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "9238dc0e0c6746aa9bf2f94c79165b53": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HBoxModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HBoxModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HBoxView",
      "box_style": "",
      "children": [
       "IPY_MODEL_89e07f16fd1448febc160dc649377478",
       "IPY_MODEL_1f47a72210494bd4ac5c0027d1fa0365",
       "IPY_MODEL_b47cc34a13c04bc180206e12ff2d0687"
      ],
      "layout": "IPY_MODEL_7aae2717cd1d446a84e49ea79c239f77"
     }
    },
    "89e07f16fd1448febc160dc649377478": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_96c5299b320147be8223c42a0cfc964d",
      "placeholder": "​",
      "style": "IPY_MODEL_513127241a1640b08c7cd4fa8aba10ac",
      "value": " 12%"
     }
    },
    "1f47a72210494bd4ac5c0027d1fa0365": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "FloatProgressModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "FloatProgressModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "ProgressView",
      "bar_style": "danger",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_f31eea249e52412fa4751cf11ddfba86",
      "max": 128,
      "min": 0,
      "orientation": "horizontal",
      "style": "IPY_MODEL_539d82b8cbb4448fbb2e80e87d4eaf84",
      "value": 15
     }
    },
    "b47cc34a13c04bc180206e12ff2d0687": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "HTMLModel",
     "model_module_version": "1.5.0",
     "state": {
      "_dom_classes": [],
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "HTMLModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/controls",
      "_view_module_version": "1.5.0",
      "_view_name": "HTMLView",
      "description": "",
      "description_tooltip": null,
      "layout": "IPY_MODEL_b1906c727a9148d5af7ff91bb2ef7da8",
      "placeholder": "​",
      "style": "IPY_MODEL_4c4b81615aef4f018b82f6a23a2ab822",
      "value": " 15/128 [00:00&lt;00:02, 51.25it/s]"
     }
    },
    "7aae2717cd1d446a84e49ea79c239f77": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "96c5299b320147be8223c42a0cfc964d": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "513127241a1640b08c7cd4fa8aba10ac": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    },
    "f31eea249e52412fa4751cf11ddfba86": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "539d82b8cbb4448fbb2e80e87d4eaf84": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "ProgressStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "ProgressStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "bar_color": null,
      "description_width": ""
     }
    },
    "b1906c727a9148d5af7ff91bb2ef7da8": {
     "model_module": "@jupyter-widgets/base",
     "model_name": "LayoutModel",
     "model_module_version": "1.2.0",
     "state": {
      "_model_module": "@jupyter-widgets/base",
      "_model_module_version": "1.2.0",
      "_model_name": "LayoutModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "LayoutView",
      "align_content": null,
      "align_items": null,
      "align_self": null,
      "border": null,
      "bottom": null,
      "display": null,
      "flex": null,
      "flex_flow": null,
      "grid_area": null,
      "grid_auto_columns": null,
      "grid_auto_flow": null,
      "grid_auto_rows": null,
      "grid_column": null,
      "grid_gap": null,
      "grid_row": null,
      "grid_template_areas": null,
      "grid_template_columns": null,
      "grid_template_rows": null,
      "height": null,
      "justify_content": null,
      "justify_items": null,
      "left": null,
      "margin": null,
      "max_height": null,
      "max_width": null,
      "min_height": null,
      "min_width": null,
      "object_fit": null,
      "object_position": null,
      "order": null,
      "overflow": null,
      "overflow_x": null,
      "overflow_y": null,
      "padding": null,
      "right": null,
      "top": null,
      "visibility": null,
      "width": null
     }
    },
    "4c4b81615aef4f018b82f6a23a2ab822": {
     "model_module": "@jupyter-widgets/controls",
     "model_name": "DescriptionStyleModel",
     "model_module_version": "1.5.0",
     "state": {
      "_model_module": "@jupyter-widgets/controls",
      "_model_module_version": "1.5.0",
      "_model_name": "DescriptionStyleModel",
      "_view_count": null,
      "_view_module": "@jupyter-widgets/base",
      "_view_module_version": "1.2.0",
      "_view_name": "StyleView",
      "description_width": ""
     }
    }
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 0
}
